├── .clippy.toml ├── .github ├── DISCUSSION_TEMPLATE │ └── q-a.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── CI.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── ECOSYSTEM.md ├── README.md ├── axum-core ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md └── src │ ├── body.rs │ ├── error.rs │ ├── ext_traits │ ├── mod.rs │ ├── request.rs │ └── request_parts.rs │ ├── extract │ ├── default_body_limit.rs │ ├── from_ref.rs │ ├── mod.rs │ ├── option.rs │ ├── rejection.rs │ ├── request_parts.rs │ └── tuple.rs │ ├── lib.rs │ ├── macros.rs │ └── response │ ├── append_headers.rs │ ├── into_response.rs │ ├── into_response_parts.rs │ └── mod.rs ├── axum-extra ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── src │ ├── body │ │ ├── async_read_body.rs │ │ └── mod.rs │ ├── either.rs │ ├── extract │ │ ├── cached.rs │ │ ├── cookie │ │ │ ├── mod.rs │ │ │ ├── private.rs │ │ │ └── signed.rs │ │ ├── form.rs │ │ ├── host.rs │ │ ├── json_deserializer.rs │ │ ├── mod.rs │ │ ├── multipart.rs │ │ ├── optional_path.rs │ │ ├── query.rs │ │ ├── rejection.rs │ │ ├── scheme.rs │ │ └── with_rejection.rs │ ├── handler │ │ ├── mod.rs │ │ └── or.rs │ ├── json_lines.rs │ ├── lib.rs │ ├── middleware.rs │ ├── protobuf.rs │ ├── response │ │ ├── attachment.rs │ │ ├── erased_json.rs │ │ ├── error_response.rs │ │ ├── file_stream.rs │ │ ├── mod.rs │ │ └── multiple.rs │ ├── routing │ │ ├── mod.rs │ │ ├── resource.rs │ │ └── typed.rs │ └── typed_header.rs └── test_files │ ├── index.html │ ├── index_2.html │ └── script.js ├── axum-macros ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── rust-toolchain ├── src │ ├── attr_parsing.rs │ ├── axum_test.rs │ ├── debug_handler.rs │ ├── from_ref.rs │ ├── from_request │ │ ├── attr.rs │ │ └── mod.rs │ ├── lib.rs │ ├── typed_path.rs │ └── with_position.rs └── tests │ ├── debug_handler │ ├── fail │ │ ├── .gitkeep │ │ ├── argument_not_extractor.rs │ │ ├── argument_not_extractor.stderr │ │ ├── duplicate_args.rs │ │ ├── duplicate_args.stderr │ │ ├── extension_not_clone.rs │ │ ├── extension_not_clone.stderr │ │ ├── extract_self_mut.rs │ │ ├── extract_self_mut.stderr │ │ ├── extract_self_ref.rs │ │ ├── extract_self_ref.stderr │ │ ├── generics.rs │ │ ├── generics.stderr │ │ ├── invalid_attrs.rs │ │ ├── invalid_attrs.stderr │ │ ├── json_not_deserialize.rs │ │ ├── json_not_deserialize.stderr │ │ ├── multiple_paths.rs │ │ ├── multiple_paths.stderr │ │ ├── multiple_request_consumers.rs │ │ ├── multiple_request_consumers.stderr │ │ ├── not_a_function.rs │ │ ├── not_a_function.stderr │ │ ├── not_async.rs │ │ ├── not_async.stderr │ │ ├── not_send.rs │ │ ├── not_send.stderr │ │ ├── output_tuple_too_many.rs │ │ ├── output_tuple_too_many.stderr │ │ ├── returning_request_parts.rs │ │ ├── returning_request_parts.stderr │ │ ├── single_wrong_return_tuple.rs │ │ ├── single_wrong_return_tuple.stderr │ │ ├── too_many_extractors.rs │ │ ├── too_many_extractors.stderr │ │ ├── wrong_order.rs │ │ ├── wrong_order.stderr │ │ ├── wrong_return_tuple.rs │ │ ├── wrong_return_tuple.stderr │ │ ├── wrong_return_type.rs │ │ └── wrong_return_type.stderr │ └── pass │ │ ├── associated_fn_without_self.rs │ │ ├── deny_unreachable_code.rs │ │ ├── impl_future.rs │ │ ├── impl_into_response.rs │ │ ├── infer_state.rs │ │ ├── multiple_extractors.rs │ │ ├── mut_extractor.rs │ │ ├── ready.rs │ │ ├── request_last.rs │ │ ├── result_impl_into_response.rs │ │ ├── returns_self.rs │ │ ├── self_receiver.rs │ │ ├── set_state.rs │ │ └── state_and_body.rs │ ├── debug_middleware │ ├── fail │ │ ├── doesnt_take_next.rs │ │ ├── doesnt_take_next.stderr │ │ ├── next_not_last.rs │ │ ├── next_not_last.stderr │ │ ├── takes_next_twice.rs │ │ └── takes_next_twice.stderr │ └── pass │ │ └── basic.rs │ ├── from_ref │ ├── fail │ │ ├── generics.rs │ │ └── generics.stderr │ └── pass │ │ ├── basic.rs │ │ ├── reference-types.rs │ │ └── skip.rs │ ├── from_request │ ├── fail │ │ ├── double_via_attr.rs │ │ ├── double_via_attr.stderr │ │ ├── enum_from_request_ident_in_variant.rs │ │ ├── enum_from_request_ident_in_variant.stderr │ │ ├── enum_from_request_on_variant.rs │ │ ├── enum_from_request_on_variant.stderr │ │ ├── enum_no_via.rs │ │ ├── enum_no_via.stderr │ │ ├── generic.rs │ │ ├── generic.stderr │ │ ├── generic_without_via.rs │ │ ├── generic_without_via.stderr │ │ ├── generic_without_via_rejection.rs │ │ ├── generic_without_via_rejection.stderr │ │ ├── not_enum_or_struct.rs │ │ ├── not_enum_or_struct.stderr │ │ ├── override_rejection_on_enum_without_via.rs │ │ ├── override_rejection_on_enum_without_via.stderr │ │ ├── parts_extracting_body.rs │ │ ├── parts_extracting_body.stderr │ │ ├── state_infer_multiple_different_types.rs │ │ ├── state_infer_multiple_different_types.stderr │ │ ├── unknown_attr_container.rs │ │ ├── unknown_attr_container.stderr │ │ ├── unknown_attr_field.rs │ │ ├── unknown_attr_field.stderr │ │ ├── via_on_container_and_field.rs │ │ └── via_on_container_and_field.stderr │ └── pass │ │ ├── container.rs │ │ ├── container_parts.rs │ │ ├── empty_named.rs │ │ ├── empty_named_parts.rs │ │ ├── empty_tuple.rs │ │ ├── empty_tuple_parts.rs │ │ ├── enum_via.rs │ │ ├── enum_via_parts.rs │ │ ├── named.rs │ │ ├── named_parts.rs │ │ ├── named_via.rs │ │ ├── named_via_parts.rs │ │ ├── override_rejection.rs │ │ ├── override_rejection_non_generic.rs │ │ ├── override_rejection_non_generic_parts.rs │ │ ├── override_rejection_parts.rs │ │ ├── override_rejection_with_via_on_enum.rs │ │ ├── override_rejection_with_via_on_enum_parts.rs │ │ ├── override_rejection_with_via_on_struct.rs │ │ ├── override_rejection_with_via_on_struct_parts.rs │ │ ├── state_cookie.rs │ │ ├── state_enum_via.rs │ │ ├── state_enum_via_parts.rs │ │ ├── state_explicit.rs │ │ ├── state_explicit_parts.rs │ │ ├── state_field_explicit.rs │ │ ├── state_field_infer.rs │ │ ├── state_infer.rs │ │ ├── state_infer_multiple.rs │ │ ├── state_infer_parts.rs │ │ ├── state_via.rs │ │ ├── state_via_infer.rs │ │ ├── state_via_parts.rs │ │ ├── state_with_rejection.rs │ │ ├── tuple.rs │ │ ├── tuple_parts.rs │ │ ├── tuple_same_type_twice.rs │ │ ├── tuple_same_type_twice_parts.rs │ │ ├── tuple_same_type_twice_via.rs │ │ ├── tuple_same_type_twice_via_parts.rs │ │ ├── tuple_via.rs │ │ ├── tuple_via_parts.rs │ │ ├── unit.rs │ │ └── unit_parts.rs │ └── typed_path │ ├── fail │ ├── missing_capture.rs │ ├── missing_capture.stderr │ ├── missing_field.rs │ ├── missing_field.stderr │ ├── not_deserialize.rs │ ├── not_deserialize.stderr │ ├── route_not_starting_with_slash.rs │ ├── route_not_starting_with_slash.stderr │ ├── route_not_starting_with_slash_non_empty.rs │ ├── route_not_starting_with_slash_non_empty.stderr │ ├── unit_with_capture.rs │ └── unit_with_capture.stderr │ └── pass │ ├── customize_rejection.rs │ ├── into_uri.rs │ ├── named_fields_struct.rs │ ├── result_handler.rs │ ├── tuple_struct.rs │ ├── unit_struct.rs │ ├── url_encoding.rs │ └── wildcards.rs ├── axum ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── benches │ └── benches.rs ├── src │ ├── body │ │ └── mod.rs │ ├── boxed.rs │ ├── docs │ │ ├── debugging_handler_type_errors.md │ │ ├── error_handling.md │ │ ├── extract.md │ │ ├── handlers_intro.md │ │ ├── method_routing │ │ │ ├── fallback.md │ │ │ ├── layer.md │ │ │ ├── merge.md │ │ │ └── route_layer.md │ │ ├── middleware.md │ │ ├── response.md │ │ └── routing │ │ │ ├── fallback.md │ │ │ ├── into_make_service_with_connect_info.md │ │ │ ├── layer.md │ │ │ ├── merge.md │ │ │ ├── method_not_allowed_fallback.md │ │ │ ├── nest.md │ │ │ ├── route.md │ │ │ ├── route_layer.md │ │ │ ├── route_service.md │ │ │ ├── with_state.md │ │ │ └── without_v07_checks.md │ ├── error_handling │ │ └── mod.rs │ ├── extension.rs │ ├── extract │ │ ├── connect_info.rs │ │ ├── matched_path.rs │ │ ├── mod.rs │ │ ├── multipart.rs │ │ ├── nested_path.rs │ │ ├── original_uri.rs │ │ ├── path │ │ │ ├── de.rs │ │ │ └── mod.rs │ │ ├── query.rs │ │ ├── raw_form.rs │ │ ├── raw_query.rs │ │ ├── rejection.rs │ │ ├── state.rs │ │ └── ws.rs │ ├── form.rs │ ├── handler │ │ ├── future.rs │ │ ├── mod.rs │ │ └── service.rs │ ├── json.rs │ ├── lib.rs │ ├── macros.rs │ ├── middleware │ │ ├── from_extractor.rs │ │ ├── from_fn.rs │ │ ├── map_request.rs │ │ ├── map_response.rs │ │ └── mod.rs │ ├── response │ │ ├── mod.rs │ │ ├── redirect.rs │ │ └── sse.rs │ ├── routing │ │ ├── future.rs │ │ ├── into_make_service.rs │ │ ├── method_filter.rs │ │ ├── method_routing.rs │ │ ├── mod.rs │ │ ├── not_found.rs │ │ ├── path_router.rs │ │ ├── route.rs │ │ ├── strip_prefix.rs │ │ ├── tests │ │ │ ├── fallback.rs │ │ │ ├── get_to_head.rs │ │ │ ├── handle_error.rs │ │ │ ├── merge.rs │ │ │ ├── mod.rs │ │ │ └── nest.rs │ │ └── url_params.rs │ ├── serve │ │ ├── listener.rs │ │ └── mod.rs │ ├── service_ext.rs │ ├── test_helpers │ │ ├── counting_cloneable_state.rs │ │ ├── mod.rs │ │ ├── test_client.rs │ │ └── tracing_helpers.rs │ └── util.rs └── tests │ └── panic_location.rs ├── deny.toml └── examples ├── README.md ├── anyhow-error-response ├── Cargo.toml └── src │ └── main.rs ├── async-graphql └── README.md ├── auto-reload ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── chat ├── Cargo.toml ├── chat.html └── src │ └── main.rs ├── compression ├── Cargo.toml ├── README.md ├── data │ ├── products.json │ └── products.json.gz └── src │ ├── main.rs │ └── tests.rs ├── consume-body-in-extractor-or-middleware ├── Cargo.toml └── src │ └── main.rs ├── cors ├── Cargo.toml └── src │ └── main.rs ├── customize-extractor-error ├── Cargo.toml ├── README.md └── src │ ├── custom_extractor.rs │ ├── derive_from_request.rs │ ├── main.rs │ └── with_rejection.rs ├── customize-path-rejection ├── Cargo.toml └── src │ └── main.rs ├── dependency-injection ├── Cargo.toml └── src │ └── main.rs ├── diesel-async-postgres ├── Cargo.toml ├── migrations │ └── 2023-03-14-180127_add_users │ │ ├── down.sql │ │ └── up.sql └── src │ └── main.rs ├── diesel-postgres ├── Cargo.toml ├── migrations │ └── 2023-03-14-180127_add_users │ │ ├── down.sql │ │ └── up.sql └── src │ └── main.rs ├── error-handling ├── Cargo.toml └── src │ └── main.rs ├── form ├── Cargo.toml └── src │ └── main.rs ├── global-404-handler ├── Cargo.toml └── src │ └── main.rs ├── graceful-shutdown ├── Cargo.toml └── src │ └── main.rs ├── handle-head-request ├── Cargo.toml └── src │ └── main.rs ├── hello-world ├── Cargo.toml └── src │ └── main.rs ├── http-proxy ├── Cargo.toml └── src │ └── main.rs ├── jwt ├── Cargo.toml └── src │ └── main.rs ├── key-value-store ├── Cargo.toml └── src │ └── main.rs ├── low-level-native-tls ├── Cargo.toml ├── self_signed_certs │ ├── cert.pem │ └── key.pem └── src │ └── main.rs ├── low-level-openssl ├── Cargo.toml ├── self_signed_certs │ ├── cert.pem │ └── key.pem └── src │ └── main.rs ├── low-level-rustls ├── Cargo.toml ├── self_signed_certs │ ├── cert.pem │ └── key.pem └── src │ └── main.rs ├── mongodb ├── Cargo.toml └── src │ └── main.rs ├── multipart-form ├── Cargo.toml └── src │ └── main.rs ├── oauth ├── Cargo.toml └── src │ └── main.rs ├── parse-body-based-on-content-type ├── Cargo.toml └── src │ └── main.rs ├── print-request-response ├── Cargo.toml └── src │ └── main.rs ├── prometheus-metrics ├── Cargo.toml └── src │ └── main.rs ├── query-params-with-empty-strings ├── Cargo.toml └── src │ └── main.rs ├── readme ├── Cargo.toml └── src │ └── main.rs ├── request-id ├── Cargo.toml └── src │ └── main.rs ├── reqwest-response ├── Cargo.toml └── src │ └── main.rs ├── reverse-proxy ├── Cargo.toml └── src │ └── main.rs ├── routes-and-handlers-close-together ├── Cargo.toml └── src │ └── main.rs ├── serve-with-hyper ├── Cargo.toml └── src │ └── main.rs ├── simple-router-wasm ├── Cargo.toml └── src │ └── main.rs ├── sqlx-postgres ├── Cargo.toml └── src │ └── main.rs ├── sse ├── Cargo.toml ├── assets │ ├── index.html │ └── script.js └── src │ └── main.rs ├── static-file-server ├── Cargo.toml ├── assets │ ├── index.html │ └── script.js └── src │ └── main.rs ├── stream-to-file ├── Cargo.toml └── src │ └── main.rs ├── templates-minijinja ├── Cargo.toml ├── src │ └── main.rs └── templates │ ├── about.jinja │ ├── content.jinja │ ├── home.jinja │ └── layout.jinja ├── templates ├── Cargo.toml ├── src │ └── main.rs └── templates │ └── hello.html ├── testing-websockets ├── Cargo.toml └── src │ └── main.rs ├── testing ├── Cargo.toml └── src │ └── main.rs ├── tls-graceful-shutdown ├── Cargo.toml ├── self_signed_certs │ ├── cert.pem │ └── key.pem └── src │ └── main.rs ├── tls-rustls ├── Cargo.toml ├── self_signed_certs │ ├── cert.pem │ └── key.pem └── src │ └── main.rs ├── todos ├── Cargo.toml └── src │ └── main.rs ├── tokio-postgres ├── Cargo.toml └── src │ └── main.rs ├── tokio-redis ├── Cargo.toml └── src │ └── main.rs ├── tracing-aka-logging ├── Cargo.toml └── src │ └── main.rs ├── unix-domain-socket ├── Cargo.toml └── src │ └── main.rs ├── validator ├── Cargo.toml └── src │ └── main.rs ├── versioning ├── Cargo.toml └── src │ └── main.rs ├── websockets-http2 ├── Cargo.toml ├── assets │ ├── index.html │ └── script.js ├── self_signed_certs │ ├── cert.pem │ └── key.pem └── src │ └── main.rs └── websockets ├── Cargo.toml ├── assets ├── index.html └── script.js └── src ├── client.rs └── main.rs /.clippy.toml: -------------------------------------------------------------------------------- 1 | allow-mixed-uninlined-format-args = false 2 | disallowed-types = [ 3 | { path = "tower::util::BoxCloneService", reason = "Use tower::util::BoxCloneSyncService instead" }, 4 | ] 5 | -------------------------------------------------------------------------------- /.github/DISCUSSION_TEMPLATE/q-a.yml: -------------------------------------------------------------------------------- 1 | body: 2 | - type: textarea 3 | attributes: 4 | label: Summary 5 | description: 'Your question:' 6 | validations: 7 | required: true 8 | - type: input 9 | attributes: 10 | label: axum version 11 | description: 'Please look it up in `Cargo.lock`, or as described below' 12 | validations: 13 | required: true 14 | - type: markdown 15 | attributes: 16 | value: | 17 | > If you have `jq` installed, you can look up the version by running 18 | > 19 | > ```bash 20 | > cargo metadata --format-version=1 | jq -r '.packages[] | select(.name == "axum") | .version' 21 | > ``` 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug Report 3 | about: If something isn't working as expected 🤔. 4 | --- 5 | 6 | 11 | 12 | - [ ] I have looked for existing issues (including closed) about this 13 | 14 | ## Bug Report 15 | 16 | ### Version 17 | 18 | 24 | 25 | ### Platform 26 | 27 | 30 | 31 | ### Crates 32 | 33 | 37 | 38 | ### Description 39 | 40 | 55 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - name: 🙏 Q&A (GitHub Discussions) 3 | url: https://github.com/tokio-rs/axum/discussions/categories/q-a 4 | about: Q&A all around axum usage 5 | - name: 💬 Tokio Discord 6 | url: https://discord.gg/tokio 7 | about: Community chat for Tokio (axum channel is under libs) 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 💡 Feature Request 3 | about: I have a suggestion (and may want to implement it 🙂)! 4 | --- 5 | 6 | - [ ] I have looked for existing issues (including closed) about this 7 | 8 | ## Feature Request 9 | 10 | ### Motivation 11 | 12 | 15 | 16 | ### Proposal 17 | 18 | 22 | 23 | ### Alternatives 24 | 25 | 30 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 9 | 10 | ## Motivation 11 | 12 | 17 | 18 | ## Solution 19 | 20 | 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .DS_Store 3 | .vscode 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | axum's changelog has moved and now lives [here](https://github.com/tokio-rs/axum/blob/main/axum/CHANGELOG.md). 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["axum", "axum-*", "examples/*"] 3 | # Only check / build main crates by default (check all with `--workspace`) 4 | default-members = ["axum", "axum-*"] 5 | # Example has been deleted, but README.md remains 6 | exclude = ["examples/async-graphql"] 7 | resolver = "2" 8 | 9 | [workspace.package] 10 | rust-version = "1.75" 11 | 12 | [workspace.lints.rust] 13 | unsafe_code = "forbid" 14 | 15 | rust_2018_idioms = { level = "warn", priority = -1 } 16 | missing_debug_implementations = "warn" 17 | missing_docs = "warn" 18 | unreachable_pub = "warn" 19 | 20 | [workspace.lints.clippy] 21 | type_complexity = "allow" 22 | 23 | await_holding_lock = "warn" 24 | dbg_macro = "warn" 25 | empty_enum = "warn" 26 | enum_glob_use = "warn" 27 | exit = "warn" 28 | filter_map_next = "warn" 29 | fn_params_excessive_bools = "warn" 30 | if_let_mutex = "warn" 31 | imprecise_flops = "warn" 32 | inefficient_to_string = "warn" 33 | linkedlist = "warn" 34 | lossy_float_literal = "warn" 35 | macro_use_imports = "warn" 36 | match_wildcard_for_single_variants = "warn" 37 | mem_forget = "warn" 38 | needless_borrow = "warn" 39 | needless_continue = "warn" 40 | option_option = "warn" 41 | rest_pat_in_fully_bound_structs = "warn" 42 | str_to_string = "warn" 43 | suboptimal_flops = "warn" 44 | todo = "warn" 45 | uninlined_format_args = "warn" 46 | unnested_or_patterns = "warn" 47 | unused_self = "warn" 48 | verbose_file_reads = "warn" 49 | 50 | # configuration for https://github.com/crate-ci/typos 51 | [workspace.metadata.typos.default.extend-identifiers] 52 | # These have been fixed in the past, but are still present in the changelog. 53 | DefaultOnFailedUpdgrade = "DefaultOnFailedUpdgrade" 54 | OnFailedUpdgrade = "OnFailedUpdgrade" 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | axum/README.md -------------------------------------------------------------------------------- /axum-core/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 axum Contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /axum-core/README.md: -------------------------------------------------------------------------------- 1 | # axum-core 2 | 3 | [![Build status](https://github.com/tokio-rs/axum/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/tokio-rs/axum-core/actions/workflows/CI.yml) 4 | [![Crates.io](https://img.shields.io/crates/v/axum-core)](https://crates.io/crates/axum-core) 5 | [![Documentation](https://docs.rs/axum-core/badge.svg)](https://docs.rs/axum-core) 6 | 7 | Core types and traits for axum. 8 | 9 | More information about this crate can be found in the [crate documentation][docs]. 10 | 11 | ## Safety 12 | 13 | This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in 100% safe Rust. 14 | 15 | ## Minimum supported Rust version 16 | 17 | axum-core's MSRV is 1.75. 18 | 19 | ## Getting Help 20 | 21 | You're also welcome to ask in the [Discord channel][chat] or open an [issue] 22 | with your question. 23 | 24 | ## Contributing 25 | 26 | 🎈 Thanks for your help improving the project! We are so happy to have 27 | you! We have a [contributing guide][contributing] to help you get involved in the 28 | `axum` project. 29 | 30 | ## License 31 | 32 | This project is licensed under the [MIT license][license]. 33 | 34 | ### Contribution 35 | 36 | Unless you explicitly state otherwise, any contribution intentionally submitted 37 | for inclusion in `axum` by you, shall be licensed as MIT, without any 38 | additional terms or conditions. 39 | 40 | [`axum`]: https://crates.io/crates/axum 41 | [chat]: https://discord.gg/tokio 42 | [contributing]: /CONTRIBUTING.md 43 | [docs]: https://docs.rs/axum-core 44 | [license]: /axum-core/LICENSE 45 | [issue]: https://github.com/tokio-rs/axum/issues/new 46 | -------------------------------------------------------------------------------- /axum-core/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::BoxError; 2 | use std::{error::Error as StdError, fmt}; 3 | 4 | /// Errors that can happen when using axum. 5 | #[derive(Debug)] 6 | pub struct Error { 7 | inner: BoxError, 8 | } 9 | 10 | impl Error { 11 | /// Create a new `Error` from a boxable error. 12 | pub fn new(error: impl Into) -> Self { 13 | Self { 14 | inner: error.into(), 15 | } 16 | } 17 | 18 | /// Convert an `Error` back into the underlying boxed trait object. 19 | pub fn into_inner(self) -> BoxError { 20 | self.inner 21 | } 22 | } 23 | 24 | impl fmt::Display for Error { 25 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 26 | self.inner.fmt(f) 27 | } 28 | } 29 | 30 | impl StdError for Error { 31 | fn source(&self) -> Option<&(dyn StdError + 'static)> { 32 | Some(&*self.inner) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /axum-core/src/ext_traits/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod request; 2 | pub(crate) mod request_parts; 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | use std::convert::Infallible; 7 | 8 | use crate::extract::{FromRef, FromRequestParts}; 9 | use http::request::Parts; 10 | 11 | #[derive(Debug, Default, Clone, Copy)] 12 | pub(crate) struct State(pub(crate) S); 13 | 14 | impl FromRequestParts for State 15 | where 16 | InnerState: FromRef, 17 | OuterState: Send + Sync, 18 | { 19 | type Rejection = Infallible; 20 | 21 | async fn from_request_parts( 22 | _parts: &mut Parts, 23 | state: &OuterState, 24 | ) -> Result { 25 | let inner_state = InnerState::from_ref(state); 26 | Ok(Self(inner_state)) 27 | } 28 | } 29 | 30 | // some extractor that requires the state, such as `SignedCookieJar` 31 | #[allow(dead_code)] 32 | pub(crate) struct RequiresState(pub(crate) String); 33 | 34 | impl FromRequestParts for RequiresState 35 | where 36 | S: Send + Sync, 37 | String: FromRef, 38 | { 39 | type Rejection = Infallible; 40 | 41 | async fn from_request_parts( 42 | _parts: &mut Parts, 43 | state: &S, 44 | ) -> Result { 45 | Ok(Self(String::from_ref(state))) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /axum-core/src/extract/from_ref.rs: -------------------------------------------------------------------------------- 1 | /// Used to do reference-to-value conversions thus not consuming the input value. 2 | /// 3 | /// This is mainly used with [`State`] to extract "substates" from a reference to main application 4 | /// state. 5 | /// 6 | /// See [`State`] for more details on how library authors should use this trait. 7 | /// 8 | /// This trait can be derived using `#[derive(FromRef)]`. 9 | /// 10 | /// [`State`]: https://docs.rs/axum/0.8/axum/extract/struct.State.html 11 | // NOTE: This trait is defined in axum-core, even though it is mainly used with `State` which is 12 | // defined in axum. That allows crate authors to use it when implementing extractors. 13 | pub trait FromRef { 14 | /// Converts to this type from a reference to the input type. 15 | fn from_ref(input: &T) -> Self; 16 | } 17 | 18 | impl FromRef for T 19 | where 20 | T: Clone, 21 | { 22 | fn from_ref(input: &T) -> Self { 23 | input.clone() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /axum-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Core types and traits for [`axum`]. 2 | //! 3 | //! Libraries authors that want to provide [`FromRequest`] or [`IntoResponse`] implementations 4 | //! should depend on the [`axum-core`] crate, instead of `axum` if possible. 5 | //! 6 | //! [`FromRequest`]: crate::extract::FromRequest 7 | //! [`IntoResponse`]: crate::response::IntoResponse 8 | //! [`axum`]: https://crates.io/crates/axum 9 | //! [`axum-core`]: http://crates.io/crates/axum-core 10 | 11 | #![cfg_attr(test, allow(clippy::float_cmp))] 12 | #![cfg_attr(not(test), warn(clippy::print_stdout, clippy::dbg_macro))] 13 | 14 | #[macro_use] 15 | pub(crate) mod macros; 16 | #[doc(hidden)] // macro helpers 17 | pub mod __private { 18 | #[cfg(feature = "tracing")] 19 | pub use tracing; 20 | } 21 | 22 | mod error; 23 | mod ext_traits; 24 | pub use self::error::Error; 25 | 26 | pub mod body; 27 | pub mod extract; 28 | pub mod response; 29 | 30 | /// Alias for a type-erased error type. 31 | pub type BoxError = Box; 32 | 33 | pub use self::ext_traits::{request::RequestExt, request_parts::RequestPartsExt}; 34 | 35 | #[cfg(test)] 36 | use axum_macros::__private_axum_test as test; 37 | -------------------------------------------------------------------------------- /axum-extra/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 axum Contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /axum-extra/README.md: -------------------------------------------------------------------------------- 1 | # axum-extra 2 | 3 | [![Build status](https://github.com/tokio-rs/axum/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/tokio-rs/axum-extra/actions/workflows/CI.yml) 4 | [![Crates.io](https://img.shields.io/crates/v/axum-extra)](https://crates.io/crates/axum-extra) 5 | [![Documentation](https://docs.rs/axum-extra/badge.svg)](https://docs.rs/axum-extra) 6 | 7 | Extra utilities for [`axum`]. 8 | 9 | More information about this crate can be found in the [crate documentation][docs]. 10 | 11 | ## Safety 12 | 13 | This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in 100% safe Rust. 14 | 15 | ## Minimum supported Rust version 16 | 17 | axum-extra's MSRV is 1.75. 18 | 19 | ## Getting Help 20 | 21 | You're also welcome to ask in the [Discord channel][chat] or open an [issue] 22 | with your question. 23 | 24 | ## Contributing 25 | 26 | 🎈 Thanks for your help improving the project! We are so happy to have 27 | you! We have a [contributing guide][contributing] to help you get involved in the 28 | `axum` project. 29 | 30 | ## License 31 | 32 | This project is licensed under the [MIT license][license]. 33 | 34 | ### Contribution 35 | 36 | Unless you explicitly state otherwise, any contribution intentionally submitted 37 | for inclusion in `axum` by you, shall be licensed as MIT, without any 38 | additional terms or conditions. 39 | 40 | [`axum`]: https://crates.io/crates/axum 41 | [chat]: https://discord.gg/tokio 42 | [contributing]: /CONTRIBUTING.md 43 | [docs]: https://docs.rs/axum-extra 44 | [license]: /axum-extra/LICENSE 45 | [issue]: https://github.com/tokio-rs/axum/issues/new 46 | -------------------------------------------------------------------------------- /axum-extra/src/body/mod.rs: -------------------------------------------------------------------------------- 1 | //! Additional bodies. 2 | 3 | #[cfg(feature = "async-read-body")] 4 | mod async_read_body; 5 | 6 | #[cfg(feature = "async-read-body")] 7 | pub use self::async_read_body::AsyncReadBody; 8 | -------------------------------------------------------------------------------- /axum-extra/src/extract/mod.rs: -------------------------------------------------------------------------------- 1 | //! Additional extractors. 2 | 3 | mod cached; 4 | mod host; 5 | mod optional_path; 6 | pub mod rejection; 7 | mod with_rejection; 8 | 9 | #[cfg(feature = "form")] 10 | mod form; 11 | 12 | #[cfg(feature = "cookie")] 13 | pub mod cookie; 14 | 15 | #[cfg(feature = "json-deserializer")] 16 | mod json_deserializer; 17 | 18 | #[cfg(feature = "query")] 19 | mod query; 20 | 21 | #[cfg(feature = "multipart")] 22 | pub mod multipart; 23 | 24 | #[cfg(feature = "scheme")] 25 | mod scheme; 26 | 27 | #[allow(deprecated)] 28 | pub use self::optional_path::OptionalPath; 29 | pub use self::{cached::Cached, host::Host, with_rejection::WithRejection}; 30 | 31 | #[cfg(feature = "cookie")] 32 | pub use self::cookie::CookieJar; 33 | 34 | #[cfg(feature = "cookie-private")] 35 | pub use self::cookie::PrivateCookieJar; 36 | 37 | #[cfg(feature = "cookie-signed")] 38 | pub use self::cookie::SignedCookieJar; 39 | 40 | #[cfg(feature = "form")] 41 | pub use self::form::{Form, FormRejection}; 42 | 43 | #[cfg(feature = "query")] 44 | pub use self::query::OptionalQuery; 45 | #[cfg(feature = "query")] 46 | pub use self::query::{OptionalQueryRejection, Query, QueryRejection}; 47 | 48 | #[cfg(feature = "multipart")] 49 | pub use self::multipart::Multipart; 50 | 51 | #[cfg(feature = "scheme")] 52 | #[doc(no_inline)] 53 | pub use self::scheme::{Scheme, SchemeMissing}; 54 | 55 | #[cfg(feature = "json-deserializer")] 56 | pub use self::json_deserializer::{ 57 | JsonDataError, JsonDeserializer, JsonDeserializerRejection, JsonSyntaxError, 58 | MissingJsonContentType, 59 | }; 60 | 61 | #[cfg(feature = "json-lines")] 62 | #[doc(no_inline)] 63 | pub use crate::json_lines::JsonLines; 64 | 65 | #[cfg(feature = "typed-header")] 66 | #[doc(no_inline)] 67 | pub use crate::typed_header::TypedHeader; 68 | -------------------------------------------------------------------------------- /axum-extra/src/extract/rejection.rs: -------------------------------------------------------------------------------- 1 | //! Rejection response types. 2 | 3 | use axum_core::{ 4 | __composite_rejection as composite_rejection, __define_rejection as define_rejection, 5 | }; 6 | 7 | define_rejection! { 8 | #[status = BAD_REQUEST] 9 | #[body = "No host found in request"] 10 | /// Rejection type used if the [`Host`](super::Host) extractor is unable to 11 | /// resolve a host. 12 | pub struct FailedToResolveHost; 13 | } 14 | 15 | composite_rejection! { 16 | /// Rejection used for [`Host`](super::Host). 17 | /// 18 | /// Contains one variant for each way the [`Host`](super::Host) extractor 19 | /// can fail. 20 | pub enum HostRejection { 21 | FailedToResolveHost, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /axum-extra/src/middleware.rs: -------------------------------------------------------------------------------- 1 | //! Additional middleware utilities. 2 | 3 | use crate::either::Either; 4 | use tower_layer::Identity; 5 | 6 | /// Convert an `Option` into a [`Layer`]. 7 | /// 8 | /// If the layer is a `Some` it'll be applied, otherwise not. 9 | /// 10 | /// # Example 11 | /// 12 | /// ``` 13 | /// use axum_extra::middleware::option_layer; 14 | /// use axum::{Router, routing::get}; 15 | /// use std::time::Duration; 16 | /// use tower_http::timeout::TimeoutLayer; 17 | /// 18 | /// # let option_timeout = Some(Duration::new(10, 0)); 19 | /// let timeout_layer = option_timeout.map(TimeoutLayer::new); 20 | /// 21 | /// let app = Router::new() 22 | /// .route("/", get(|| async {})) 23 | /// .layer(option_layer(timeout_layer)); 24 | /// # let _: Router = app; 25 | /// ``` 26 | /// 27 | /// # Difference between this and [`tower::util::option_layer`] 28 | /// 29 | /// [`tower::util::option_layer`] always changes the error type to [`BoxError`] which requires 30 | /// using [`HandleErrorLayer`] when used with axum, even if the layer you're applying uses 31 | /// [`Infallible`]. 32 | /// 33 | /// `axum_extra::middleware::option_layer` on the other hand doesn't change the error type so can 34 | /// be applied directly. 35 | /// 36 | /// [`Layer`]: tower_layer::Layer 37 | /// [`BoxError`]: tower::BoxError 38 | /// [`HandleErrorLayer`]: axum::error_handling::HandleErrorLayer 39 | /// [`Infallible`]: std::convert::Infallible 40 | pub fn option_layer(layer: Option) -> Either { 41 | layer 42 | .map(Either::E1) 43 | .unwrap_or_else(|| Either::E2(Identity::new())) 44 | } 45 | -------------------------------------------------------------------------------- /axum-extra/test_files/index.html: -------------------------------------------------------------------------------- 1 |

Hello, World!

2 | -------------------------------------------------------------------------------- /axum-extra/test_files/index_2.html: -------------------------------------------------------------------------------- 1 | Hello, World! 2 | -------------------------------------------------------------------------------- /axum-extra/test_files/script.js: -------------------------------------------------------------------------------- 1 | console.log('hi') 2 | -------------------------------------------------------------------------------- /axum-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | categories = ["asynchronous", "network-programming", "web-programming"] 3 | description = "Macros for axum" 4 | edition = "2021" 5 | rust-version = { workspace = true } 6 | homepage = "https://github.com/tokio-rs/axum" 7 | keywords = ["axum"] 8 | license = "MIT" 9 | name = "axum-macros" 10 | readme = "README.md" 11 | repository = "https://github.com/tokio-rs/axum" 12 | version = "0.5.0" # remember to also bump the version that axum and axum-extra depends on 13 | 14 | [package.metadata.cargo-public-api-crates] 15 | allowed = [] 16 | 17 | [package.metadata.docs.rs] 18 | all-features = true 19 | 20 | [lib] 21 | proc-macro = true 22 | 23 | [features] 24 | default = [] 25 | __private = ["syn/visit-mut"] 26 | 27 | [dependencies] 28 | proc-macro2 = "1.0" 29 | quote = "1.0" 30 | syn = { version = "2.0", features = [ 31 | "full", 32 | "parsing", 33 | # needed for `Hash` impls 34 | "extra-traits", 35 | ] } 36 | 37 | [dev-dependencies] 38 | axum = { path = "../axum", features = ["macros"] } 39 | axum-extra = { path = "../axum-extra", features = ["typed-routing", "cookie-private", "typed-header"] } 40 | rustversion = "1.0" 41 | serde = { version = "1.0", features = ["derive"] } 42 | serde_json = "1.0" 43 | syn = { version = "2.0", features = ["full", "extra-traits"] } 44 | tokio = { version = "1.25.0", features = ["full"] } 45 | trybuild = "1.0.63" 46 | 47 | [lints] 48 | workspace = true 49 | -------------------------------------------------------------------------------- /axum-macros/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 axum Contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /axum-macros/README.md: -------------------------------------------------------------------------------- 1 | # axum-macros 2 | 3 | [![Build status](https://github.com/tokio-rs/axum/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/tokio-rs/axum-macros/actions/workflows/CI.yml) 4 | [![Crates.io](https://img.shields.io/crates/v/axum-macros)](https://crates.io/crates/axum-macros) 5 | [![Documentation](https://docs.rs/axum-macros/badge.svg)](https://docs.rs/axum-macros) 6 | 7 | Macros for [`axum`]. 8 | 9 | More information about this crate can be found in the [crate documentation][docs]. 10 | 11 | ## Safety 12 | 13 | This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in 100% safe Rust. 14 | 15 | ## Minimum supported Rust version 16 | 17 | axum-macros's MSRV is 1.75. 18 | 19 | ## Getting Help 20 | 21 | You're also welcome to ask in the [Discord channel][chat] or open an [issue] 22 | with your question. 23 | 24 | ## Contributing 25 | 26 | 🎈 Thanks for your help improving the project! We are so happy to have 27 | you! We have a [contributing guide][contributing] to help you get involved in the 28 | `axum` project. 29 | 30 | ## License 31 | 32 | This project is licensed under the [MIT license][license]. 33 | 34 | ### Contribution 35 | 36 | Unless you explicitly state otherwise, any contribution intentionally submitted 37 | for inclusion in `axum` by you, shall be licensed as MIT, without any 38 | additional terms or conditions. 39 | 40 | [`axum`]: https://crates.io/crates/axum 41 | [chat]: https://discord.gg/tokio 42 | [contributing]: /CONTRIBUTING.md 43 | [docs]: https://docs.rs/axum-macros 44 | [license]: /axum-macros/LICENSE 45 | [issue]: https://github.com/tokio-rs/axum/issues/new 46 | -------------------------------------------------------------------------------- /axum-macros/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2024-06-22 2 | -------------------------------------------------------------------------------- /axum-macros/src/axum_test.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::{format_ident, quote}; 3 | use syn::{parse::Parse, parse_quote, visit_mut::VisitMut, ItemFn}; 4 | 5 | pub(crate) fn expand(_attr: Attrs, mut item_fn: ItemFn) -> TokenStream { 6 | item_fn.attrs.push(parse_quote!(#[tokio::test])); 7 | 8 | let nest_service_fn = replace_nest_with_nest_service(item_fn.clone()); 9 | 10 | quote! { 11 | #item_fn 12 | #nest_service_fn 13 | } 14 | } 15 | 16 | pub(crate) struct Attrs; 17 | 18 | impl Parse for Attrs { 19 | fn parse(_input: syn::parse::ParseStream<'_>) -> syn::Result { 20 | Ok(Self) 21 | } 22 | } 23 | 24 | fn replace_nest_with_nest_service(mut item_fn: ItemFn) -> Option { 25 | item_fn.sig.ident = format_ident!("{}_with_nest_service", item_fn.sig.ident); 26 | 27 | let mut visitor = NestToNestService::default(); 28 | syn::visit_mut::visit_item_fn_mut(&mut visitor, &mut item_fn); 29 | 30 | (visitor.count > 0).then_some(item_fn) 31 | } 32 | 33 | #[derive(Default)] 34 | struct NestToNestService { 35 | count: usize, 36 | } 37 | 38 | impl VisitMut for NestToNestService { 39 | fn visit_expr_method_call_mut(&mut self, i: &mut syn::ExprMethodCall) { 40 | if i.method == "nest" && i.args.len() == 2 { 41 | i.method = parse_quote!(nest_service); 42 | self.count += 1; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tokio-rs/axum/7eabf7e645caf4e8e974cbdd886ab99eb2766d1d/axum-macros/tests/debug_handler/fail/.gitkeep -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/argument_not_extractor.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler] 4 | async fn handler(_foo: bool) {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/argument_not_extractor.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `bool: FromRequest<(), axum_core::extract::private::ViaParts>` is not satisfied 2 | --> tests/debug_handler/fail/argument_not_extractor.rs:4:24 3 | | 4 | 4 | async fn handler(_foo: bool) {} 5 | | ^^^^ the trait `FromRequestParts<()>` is not implemented for `bool`, which is required by `bool: FromRequest<(), _>` 6 | | 7 | = note: Function argument is not a valid axum extractor. 8 | See `https://docs.rs/axum/0.8/axum/extract/index.html` for details 9 | = help: the following other types implement trait `FromRequestParts`: 10 | `()` implements `FromRequestParts` 11 | `(T1, T2)` implements `FromRequestParts` 12 | `(T1, T2, T3)` implements `FromRequestParts` 13 | `(T1, T2, T3, T4)` implements `FromRequestParts` 14 | `(T1, T2, T3, T4, T5)` implements `FromRequestParts` 15 | `(T1, T2, T3, T4, T5, T6)` implements `FromRequestParts` 16 | `(T1, T2, T3, T4, T5, T6, T7)` implements `FromRequestParts` 17 | `(T1, T2, T3, T4, T5, T6, T7, T8)` implements `FromRequestParts` 18 | and $N others 19 | = note: required for `bool` to implement `FromRequest<(), axum_core::extract::private::ViaParts>` 20 | note: required by a bound in `__axum_macros_check_handler_0_from_request_check` 21 | --> tests/debug_handler/fail/argument_not_extractor.rs:4:24 22 | | 23 | 4 | async fn handler(_foo: bool) {} 24 | | ^^^^ required by this bound in `__axum_macros_check_handler_0_from_request_check` 25 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/duplicate_args.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler(state = (), state = ())] 4 | async fn handler() {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/duplicate_args.stderr: -------------------------------------------------------------------------------- 1 | error: `state` specified more than once 2 | --> tests/debug_handler/fail/duplicate_args.rs:3:29 3 | | 4 | 3 | #[debug_handler(state = (), state = ())] 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/extension_not_clone.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::Extension; 2 | use axum_macros::debug_handler; 3 | 4 | struct NonCloneType; 5 | 6 | #[debug_handler] 7 | async fn test_extension_non_clone(_: Extension) {} 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/extension_not_clone.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `NonCloneType: Clone` is not satisfied 2 | --> tests/debug_handler/fail/extension_not_clone.rs:7:38 3 | | 4 | 7 | async fn test_extension_non_clone(_: Extension) {} 5 | | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonCloneType`, which is required by `Extension: FromRequest<(), _>` 6 | | 7 | = help: the following other types implement trait `FromRequest`: 8 | (T1, T2) 9 | (T1, T2, T3) 10 | (T1, T2, T3, T4) 11 | (T1, T2, T3, T4, T5) 12 | (T1, T2, T3, T4, T5, T6) 13 | (T1, T2, T3, T4, T5, T6, T7) 14 | (T1, T2, T3, T4, T5, T6, T7, T8) 15 | (T1, T2, T3, T4, T5, T6, T7, T8, T9) 16 | and $N others 17 | = note: required for `Extension` to implement `FromRequestParts<()>` 18 | = note: required for `Extension` to implement `FromRequest<(), axum_core::extract::private::ViaParts>` 19 | note: required by a bound in `__axum_macros_check_test_extension_non_clone_0_from_request_check` 20 | --> tests/debug_handler/fail/extension_not_clone.rs:7:38 21 | | 22 | 7 | async fn test_extension_non_clone(_: Extension) {} 23 | | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__axum_macros_check_test_extension_non_clone_0_from_request_check` 24 | help: consider annotating `NonCloneType` with `#[derive(Clone)]` 25 | | 26 | 4 + #[derive(Clone)] 27 | 5 | struct NonCloneType; 28 | | 29 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/extract_self_mut.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::{FromRequest, Request}; 2 | use axum_macros::debug_handler; 3 | 4 | struct A; 5 | 6 | impl FromRequest for A 7 | where 8 | S: Send + Sync, 9 | { 10 | type Rejection = (); 11 | 12 | async fn from_request(_req: Request, _state: &S) -> Result { 13 | unimplemented!() 14 | } 15 | } 16 | 17 | impl A { 18 | #[debug_handler] 19 | async fn handler(&mut self) {} 20 | } 21 | 22 | fn main() {} 23 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/extract_self_mut.stderr: -------------------------------------------------------------------------------- 1 | error: Handlers must only take owned values 2 | --> tests/debug_handler/fail/extract_self_mut.rs:19:22 3 | | 4 | 19 | async fn handler(&mut self) {} 5 | | ^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/extract_self_ref.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::{FromRequest, Request}; 2 | use axum_macros::debug_handler; 3 | 4 | struct A; 5 | 6 | impl FromRequest for A 7 | where 8 | S: Send + Sync, 9 | { 10 | type Rejection = (); 11 | 12 | async fn from_request(_req: Request, _state: &S) -> Result { 13 | unimplemented!() 14 | } 15 | } 16 | 17 | impl A { 18 | #[debug_handler] 19 | async fn handler(&self) {} 20 | } 21 | 22 | fn main() {} 23 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/extract_self_ref.stderr: -------------------------------------------------------------------------------- 1 | error: Handlers must only take owned values 2 | --> tests/debug_handler/fail/extract_self_ref.rs:19:22 3 | | 4 | 19 | async fn handler(&self) {} 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/generics.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler] 4 | async fn handler(_extract: T) {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/generics.stderr: -------------------------------------------------------------------------------- 1 | error: `#[axum_macros::debug_handler]` doesn't support generic functions 2 | --> tests/debug_handler/fail/generics.rs:4:17 3 | | 4 | 4 | async fn handler(_extract: T) {} 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/invalid_attrs.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler(foo)] 4 | async fn handler() {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/invalid_attrs.stderr: -------------------------------------------------------------------------------- 1 | error: expected `state` 2 | --> tests/debug_handler/fail/invalid_attrs.rs:3:17 3 | | 4 | 3 | #[debug_handler(foo)] 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/json_not_deserialize.rs: -------------------------------------------------------------------------------- 1 | use axum::Json; 2 | use axum_macros::debug_handler; 3 | 4 | struct Struct {} 5 | 6 | #[debug_handler] 7 | async fn handler(_foo: Json) {} 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/multiple_paths.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::Path; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler] 5 | async fn handler(_: Path, _: Path) {} 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/multiple_paths.stderr: -------------------------------------------------------------------------------- 1 | error: Multiple parameters must be extracted with a tuple `Path<(_, _)>` or a struct `Path`, not by applying multiple `Path<_>` extractors 2 | --> tests/debug_handler/fail/multiple_paths.rs:5:18 3 | | 4 | 5 | async fn handler(_: Path, _: Path) {} 5 | | ^^^^^^^^^^^^^^^ 6 | 7 | error: Multiple parameters must be extracted with a tuple `Path<(_, _)>` or a struct `Path`, not by applying multiple `Path<_>` extractors 8 | --> tests/debug_handler/fail/multiple_paths.rs:5:35 9 | | 10 | 5 | async fn handler(_: Path, _: Path) {} 11 | | ^^^^^^^^^^^^^^^ 12 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/multiple_request_consumers.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | body::Bytes, 3 | http::{Method, Uri}, 4 | Json, 5 | }; 6 | use axum_macros::debug_handler; 7 | 8 | #[debug_handler] 9 | async fn one(_: Json<()>, _: String, _: Uri) {} 10 | 11 | #[debug_handler] 12 | async fn two(_: Json<()>, _: Method, _: Bytes, _: Uri, _: String) {} 13 | 14 | fn main() {} 15 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/multiple_request_consumers.stderr: -------------------------------------------------------------------------------- 1 | error: Can't have two extractors that consume the request body. `Json<_>` and `String` both do that. 2 | --> tests/debug_handler/fail/multiple_request_consumers.rs:9:14 3 | | 4 | 9 | async fn one(_: Json<()>, _: String, _: Uri) {} 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | 7 | error: Can't have more than one extractor that consume the request body. `Json<_>`, `Bytes`, and `String` all do that. 8 | --> tests/debug_handler/fail/multiple_request_consumers.rs:12:14 9 | | 10 | 12 | async fn two(_: Json<()>, _: Method, _: Bytes, _: Uri, _: String) {} 11 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 12 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/not_a_function.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler] 4 | struct A; 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/not_a_function.stderr: -------------------------------------------------------------------------------- 1 | error: expected `fn` 2 | --> tests/debug_handler/fail/not_a_function.rs:4:1 3 | | 4 | 4 | struct A; 5 | | ^^^^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/not_async.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler] 4 | fn handler() {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/not_async.stderr: -------------------------------------------------------------------------------- 1 | error: Handlers must be `async fn`s 2 | --> tests/debug_handler/fail/not_async.rs:4:1 3 | | 4 | 4 | fn handler() {} 5 | | ^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/not_send.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler] 4 | async fn handler() { 5 | let _rc = std::rc::Rc::new(()); 6 | async {}.await; 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/not_send.stderr: -------------------------------------------------------------------------------- 1 | error: future cannot be sent between threads safely 2 | --> tests/debug_handler/fail/not_send.rs:3:1 3 | | 4 | 3 | #[debug_handler] 5 | | ^^^^^^^^^^^^^^^^ future returned by `handler` is not `Send` 6 | | 7 | = help: within `impl Future`, the trait `Send` is not implemented for `Rc<()>`, which is required by `impl Future: Send` 8 | note: future is not `Send` as this value is used across an await 9 | --> tests/debug_handler/fail/not_send.rs:6:14 10 | | 11 | 5 | let _rc = std::rc::Rc::new(()); 12 | | --- has type `Rc<()>` which is not `Send` 13 | 6 | async {}.await; 14 | | ^^^^^ await occurs here, with `_rc` maybe used later 15 | note: required by a bound in `check` 16 | --> tests/debug_handler/fail/not_send.rs:3:1 17 | | 18 | 3 | #[debug_handler] 19 | | ^^^^^^^^^^^^^^^^ required by this bound in `check` 20 | = note: this error originates in the attribute macro `debug_handler` (in Nightly builds, run with -Z macro-backtrace for more info) 21 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/output_tuple_too_many.rs: -------------------------------------------------------------------------------- 1 | use axum::response::AppendHeaders; 2 | 3 | #[axum::debug_handler] 4 | async fn handler() -> ( 5 | axum::http::StatusCode, 6 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 7 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 8 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 9 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 10 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 11 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 12 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 13 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 14 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 15 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 16 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 17 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 18 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 19 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 20 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 21 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 22 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 23 | axum::http::StatusCode, 24 | ) { 25 | panic!() 26 | } 27 | 28 | fn main() {} 29 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr: -------------------------------------------------------------------------------- 1 | error: Cannot return tuples with more than 17 elements 2 | --> tests/debug_handler/fail/output_tuple_too_many.rs:4:20 3 | | 4 | 4 | async fn handler() -> ( 5 | | ____________________^ 6 | 5 | | axum::http::StatusCode, 7 | 6 | | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 8 | 7 | | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 9 | ... | 10 | 23 | | axum::http::StatusCode, 11 | 24 | | ) { 12 | | |_^ 13 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/returning_request_parts.rs: -------------------------------------------------------------------------------- 1 | #[axum::debug_handler] 2 | async fn handler() -> ( 3 | axum::http::request::Parts, // this should be response parts, not request parts 4 | axum::http::StatusCode, 5 | ) { 6 | panic!() 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/returning_request_parts.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/debug_handler/fail/returning_request_parts.rs:3:5 3 | | 4 | 3 | axum::http::request::Parts, // this should be response parts, not request parts 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | | 7 | | expected `axum::http::response::Parts`, found `axum::http::request::Parts` 8 | | expected `axum::http::response::Parts` because of return type 9 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_parens)] 2 | 3 | struct NotIntoResponse; 4 | 5 | #[axum::debug_handler] 6 | async fn handler() -> (NotIntoResponse) { 7 | panic!() 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `NotIntoResponse: IntoResponse` is not satisfied 2 | --> tests/debug_handler/fail/single_wrong_return_tuple.rs:6:23 3 | | 4 | 6 | async fn handler() -> (NotIntoResponse) { 5 | | ^^^^^^^^^^^^^^^^^ the trait `IntoResponse` is not implemented for `NotIntoResponse` 6 | | 7 | = help: the following other types implement trait `IntoResponse`: 8 | &'static [u8; N] 9 | &'static [u8] 10 | &'static str 11 | () 12 | (R,) 13 | (Response<()>, R) 14 | (Response<()>, T1, R) 15 | (Response<()>, T1, T2, R) 16 | and $N others 17 | note: required by a bound in `__axum_macros_check_handler_into_response::{closure#0}::check` 18 | --> tests/debug_handler/fail/single_wrong_return_tuple.rs:6:23 19 | | 20 | 6 | async fn handler() -> (NotIntoResponse) { 21 | | ^^^^^^^^^^^^^^^^^ required by this bound in `check` 22 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/too_many_extractors.rs: -------------------------------------------------------------------------------- 1 | use axum::http::Uri; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler] 5 | async fn handler( 6 | _e1: Uri, 7 | _e2: Uri, 8 | _e3: Uri, 9 | _e4: Uri, 10 | _e5: Uri, 11 | _e6: Uri, 12 | _e7: Uri, 13 | _e8: Uri, 14 | _e9: Uri, 15 | _e10: Uri, 16 | _e11: Uri, 17 | _e12: Uri, 18 | _e13: Uri, 19 | _e14: Uri, 20 | _e15: Uri, 21 | _e16: Uri, 22 | _e17: Uri, 23 | ) { 24 | } 25 | 26 | fn main() {} 27 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/too_many_extractors.stderr: -------------------------------------------------------------------------------- 1 | error: Handlers cannot take more than 16 arguments. Use `(a, b): (ExtractorA, ExtractorA)` to further nest extractors 2 | --> tests/debug_handler/fail/too_many_extractors.rs:6:5 3 | | 4 | 6 | / _e1: Uri, 5 | 7 | | _e2: Uri, 6 | 8 | | _e3: Uri, 7 | 9 | | _e4: Uri, 8 | ... | 9 | 21 | | _e16: Uri, 10 | 22 | | _e17: Uri, 11 | | |______________^ 12 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/wrong_order.rs: -------------------------------------------------------------------------------- 1 | use axum::{http::Uri, Json}; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler] 5 | async fn one(_: Json<()>, _: Uri) {} 6 | 7 | #[debug_handler] 8 | async fn two(_: String, _: Uri) {} 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/wrong_order.stderr: -------------------------------------------------------------------------------- 1 | error: `Json<_>` consumes the request body and thus must be the last argument to the handler function 2 | --> tests/debug_handler/fail/wrong_order.rs:5:17 3 | | 4 | 5 | async fn one(_: Json<()>, _: Uri) {} 5 | | ^^^^^^^^ 6 | 7 | error: `String` consumes the request body and thus must be the last argument to the handler function 8 | --> tests/debug_handler/fail/wrong_order.rs:8:17 9 | | 10 | 8 | async fn two(_: String, _: Uri) {} 11 | | ^^^^^^ 12 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/wrong_return_tuple.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_parens)] 2 | 3 | #[axum::debug_handler] 4 | async fn named_type() -> ( 5 | axum::http::StatusCode, 6 | axum::Json<&'static str>, 7 | axum::response::AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 8 | ) { 9 | panic!() 10 | } 11 | 12 | struct CustomIntoResponse {} 13 | impl axum::response::IntoResponse for CustomIntoResponse { 14 | fn into_response(self) -> axum::response::Response { 15 | todo!() 16 | } 17 | } 18 | #[axum::debug_handler] 19 | async fn custom_type() -> ( 20 | axum::http::StatusCode, 21 | CustomIntoResponse, 22 | axum::response::AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 23 | ) { 24 | panic!() 25 | } 26 | 27 | fn main() {} 28 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/wrong_return_type.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler] 4 | async fn handler() -> bool { 5 | false 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/wrong_return_type.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `bool: IntoResponse` is not satisfied 2 | --> tests/debug_handler/fail/wrong_return_type.rs:4:23 3 | | 4 | 4 | async fn handler() -> bool { 5 | | ^^^^ the trait `IntoResponse` is not implemented for `bool` 6 | | 7 | = help: the following other types implement trait `IntoResponse`: 8 | &'static [u8; N] 9 | &'static [u8] 10 | &'static str 11 | () 12 | (R,) 13 | (Response<()>, R) 14 | (Response<()>, T1, R) 15 | (Response<()>, T1, T2, R) 16 | and $N others 17 | note: required by a bound in `__axum_macros_check_handler_into_response::{closure#0}::check` 18 | --> tests/debug_handler/fail/wrong_return_type.rs:4:23 19 | | 20 | 4 | async fn handler() -> bool { 21 | | ^^^^ required by this bound in `check` 22 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/associated_fn_without_self.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | struct A; 4 | 5 | impl A { 6 | #[debug_handler] 7 | async fn handler() {} 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/deny_unreachable_code.rs: -------------------------------------------------------------------------------- 1 | #![deny(unreachable_code)] 2 | 3 | use axum::extract::Path; 4 | 5 | #[axum_macros::debug_handler] 6 | async fn handler(Path(_): Path) {} 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/impl_future.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | use std::future::Future; 3 | 4 | #[debug_handler] 5 | fn handler() -> impl Future { 6 | async {} 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/impl_into_response.rs: -------------------------------------------------------------------------------- 1 | use axum::response::IntoResponse; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler] 5 | async fn handler() -> impl IntoResponse { 6 | "hi!" 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/infer_state.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::State; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler] 5 | async fn handler(_: State) {} 6 | 7 | #[debug_handler] 8 | async fn handler_2(_: axum::extract::State) {} 9 | 10 | #[debug_handler] 11 | async fn handler_3(_: axum::extract::State, _: axum::extract::State) {} 12 | 13 | #[debug_handler] 14 | async fn handler_4(_: State, _: State) {} 15 | 16 | #[debug_handler] 17 | async fn handler_5(_: axum::extract::State, _: State) {} 18 | 19 | #[derive(Clone)] 20 | struct AppState; 21 | 22 | fn main() {} 23 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/multiple_extractors.rs: -------------------------------------------------------------------------------- 1 | use axum::http::{Method, Uri}; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler] 5 | async fn handler(_one: Method, _two: Uri, _three: String) {} 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/mut_extractor.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler] 4 | async fn handler(mut foo: String) -> String { 5 | foo += "bar"; 6 | foo 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/ready.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | use std::future::{ready, Ready}; 3 | 4 | #[debug_handler] 5 | fn handler() -> Ready<()> { 6 | ready(()) 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/request_last.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::{Extension, Request}; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler] 5 | async fn handler(_: Extension, _: Request) {} 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/returns_self.rs: -------------------------------------------------------------------------------- 1 | use axum::response::{IntoResponse, Response}; 2 | use axum_macros::debug_handler; 3 | 4 | struct A; 5 | 6 | impl A { 7 | #[debug_handler] 8 | async fn handler() -> Self { 9 | A 10 | } 11 | } 12 | 13 | impl IntoResponse for A { 14 | fn into_response(self) -> Response { 15 | todo!() 16 | } 17 | } 18 | 19 | fn main() {} 20 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/self_receiver.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::{FromRequest, Request}; 2 | use axum_macros::debug_handler; 3 | 4 | struct A; 5 | 6 | impl FromRequest for A 7 | where 8 | S: Send + Sync, 9 | { 10 | type Rejection = (); 11 | 12 | async fn from_request(_req: Request, _state: &S) -> Result { 13 | unimplemented!() 14 | } 15 | } 16 | 17 | impl FromRequest for Box 18 | where 19 | S: Send + Sync, 20 | { 21 | type Rejection = (); 22 | 23 | async fn from_request(_req: Request, _state: &S) -> Result { 24 | unimplemented!() 25 | } 26 | } 27 | 28 | impl A { 29 | #[debug_handler] 30 | async fn handler(self) {} 31 | 32 | #[debug_handler] 33 | async fn handler_with_qualified_self(self: Box) {} 34 | } 35 | 36 | fn main() {} 37 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/set_state.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::{FromRef, FromRequest, Request}; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler(state = AppState)] 5 | async fn handler(_: A) {} 6 | 7 | #[derive(Clone)] 8 | struct AppState; 9 | 10 | struct A; 11 | 12 | impl FromRequest for A 13 | where 14 | S: Send + Sync, 15 | AppState: FromRef, 16 | { 17 | type Rejection = (); 18 | 19 | async fn from_request(_req: Request, _state: &S) -> Result { 20 | unimplemented!() 21 | } 22 | } 23 | 24 | fn main() {} 25 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/state_and_body.rs: -------------------------------------------------------------------------------- 1 | use axum::{extract::Request, extract::State}; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler(state = AppState)] 5 | async fn handler(_: State, _: Request) {} 6 | 7 | #[derive(Clone)] 8 | struct AppState; 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_middleware/fail/doesnt_take_next.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | debug_middleware, 3 | extract::Request, 4 | response::{IntoResponse, Response}, 5 | }; 6 | 7 | #[debug_middleware] 8 | async fn my_middleware(request: Request) -> Response { 9 | let _ = request; 10 | ().into_response() 11 | } 12 | 13 | fn main() {} 14 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_middleware/fail/doesnt_take_next.stderr: -------------------------------------------------------------------------------- 1 | error: Middleware functions must take `axum::middleware::Next` as the last argument 2 | --> tests/debug_middleware/fail/doesnt_take_next.rs:7:1 3 | | 4 | 7 | #[debug_middleware] 5 | | ^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `debug_middleware` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_middleware/fail/next_not_last.rs: -------------------------------------------------------------------------------- 1 | use axum::{debug_middleware, extract::Request, middleware::Next, response::Response}; 2 | 3 | #[debug_middleware] 4 | async fn my_middleware(next: Next, request: Request) -> Response { 5 | next.run(request).await 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_middleware/fail/next_not_last.stderr: -------------------------------------------------------------------------------- 1 | error: `axum::middleware::Next` must the last argument 2 | --> tests/debug_middleware/fail/next_not_last.rs:4:24 3 | | 4 | 4 | async fn my_middleware(next: Next, request: Request) -> Response { 5 | | ^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_middleware/fail/takes_next_twice.rs: -------------------------------------------------------------------------------- 1 | use axum::{debug_middleware, extract::Request, middleware::Next, response::Response}; 2 | 3 | #[debug_middleware] 4 | async fn my_middleware(request: Request, next: Next, next2: Next) -> Response { 5 | let _ = next2; 6 | next.run(request).await 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_middleware/fail/takes_next_twice.stderr: -------------------------------------------------------------------------------- 1 | error: Middleware functions can only take one argument of type `axum::middleware::Next` 2 | --> tests/debug_middleware/fail/takes_next_twice.rs:3:1 3 | | 4 | 3 | #[debug_middleware] 5 | | ^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `debug_middleware` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_middleware/pass/basic.rs: -------------------------------------------------------------------------------- 1 | use axum::{debug_middleware, extract::Request, middleware::Next, response::Response}; 2 | 3 | #[debug_middleware] 4 | async fn my_middleware(request: Request, next: Next) -> Response { 5 | next.run(request).await 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /axum-macros/tests/from_ref/fail/generics.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::FromRef; 2 | 3 | #[derive(Clone, FromRef)] 4 | struct AppState { 5 | foo: T, 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /axum-macros/tests/from_ref/fail/generics.stderr: -------------------------------------------------------------------------------- 1 | error: `#[derive(FromRef)]` doesn't support generics 2 | --> tests/from_ref/fail/generics.rs:4:16 3 | | 4 | 4 | struct AppState { 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/from_ref/pass/basic.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRef, State}, 3 | routing::get, 4 | Router, 5 | }; 6 | 7 | // This will implement `FromRef` for each field in the struct. 8 | #[derive(Clone, FromRef)] 9 | struct AppState { 10 | auth_token: String, 11 | } 12 | 13 | // So those types can be extracted via `State` 14 | async fn handler(_: State) {} 15 | 16 | fn main() { 17 | let state = AppState { 18 | auth_token: Default::default(), 19 | }; 20 | 21 | let _: axum::Router = Router::new().route("/", get(handler)).with_state(state); 22 | } 23 | -------------------------------------------------------------------------------- /axum-macros/tests/from_ref/pass/reference-types.rs: -------------------------------------------------------------------------------- 1 | #![deny(noop_method_call)] 2 | 3 | use axum_macros::FromRef; 4 | 5 | #[derive(FromRef)] 6 | struct State { 7 | inner: &'static str, 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/from_ref/pass/skip.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRef; 2 | 3 | #[derive(Clone, FromRef)] 4 | struct AppState { 5 | auth_token: String, 6 | #[from_ref(skip)] 7 | also_string: String, 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/double_via_attr.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | struct Extractor(#[from_request(via(axum::Extension), via(axum::Extension))] State); 5 | 6 | #[derive(Clone)] 7 | struct State; 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/double_via_attr.stderr: -------------------------------------------------------------------------------- 1 | error: `via` specified more than once 2 | --> tests/from_request/fail/double_via_attr.rs:4:55 3 | | 4 | 4 | struct Extractor(#[from_request(via(axum::Extension), via(axum::Extension))] State); 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/enum_from_request_ident_in_variant.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest, Clone)] 4 | #[from_request(via(axum::Extension))] 5 | enum Extractor { 6 | Foo { 7 | #[from_request(via(axum::Extension))] 8 | foo: (), 9 | }, 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/enum_from_request_ident_in_variant.stderr: -------------------------------------------------------------------------------- 1 | error: `#[from_request(via(...))]` cannot be used inside variants 2 | --> tests/from_request/fail/enum_from_request_ident_in_variant.rs:7:24 3 | | 4 | 7 | #[from_request(via(axum::Extension))] 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/enum_from_request_on_variant.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest, Clone)] 4 | #[from_request(via(axum::Extension))] 5 | enum Extractor { 6 | #[from_request(via(axum::Extension))] 7 | Foo, 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/enum_from_request_on_variant.stderr: -------------------------------------------------------------------------------- 1 | error: `#[from_request(via(...))]` cannot be used on variants 2 | --> tests/from_request/fail/enum_from_request_on_variant.rs:6:20 3 | | 4 | 6 | #[from_request(via(axum::Extension))] 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/enum_no_via.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest, Clone)] 4 | enum Extractor {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/enum_no_via.stderr: -------------------------------------------------------------------------------- 1 | error: missing `#[from_request(via(...))]` 2 | --> tests/from_request/fail/enum_no_via.rs:3:10 3 | | 4 | 3 | #[derive(FromRequest, Clone)] 5 | | ^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `FromRequest` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/generic.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | struct Extractor(Option); 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/generic.stderr: -------------------------------------------------------------------------------- 1 | error: #[derive(FromRequest)] only supports generics on tuple structs that have exactly one field of the generic type 2 | --> tests/from_request/fail/generic.rs:4:21 3 | | 4 | 4 | struct Extractor(Option); 5 | | ^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/generic_without_via.rs: -------------------------------------------------------------------------------- 1 | use axum::{routing::get, Router}; 2 | use axum_macros::FromRequest; 3 | 4 | #[derive(FromRequest, Clone)] 5 | struct Extractor(T); 6 | 7 | async fn foo(_: Extractor<()>) {} 8 | 9 | fn main() { 10 | _ = Router::<()>::new().route("/", get(foo)); 11 | } 12 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/generic_without_via.stderr: -------------------------------------------------------------------------------- 1 | error: #[derive(FromRequest)] only supports generics when used with #[from_request(via)] 2 | --> tests/from_request/fail/generic_without_via.rs:5:18 3 | | 4 | 5 | struct Extractor(T); 5 | | ^ 6 | 7 | error[E0277]: the trait bound `fn(Extractor<()>) -> impl Future {foo}: Handler<_, _>` is not satisfied 8 | --> tests/from_request/fail/generic_without_via.rs:10:44 9 | | 10 | 10 | _ = Router::<()>::new().route("/", get(foo)); 11 | | --- ^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Extractor<()>) -> impl Future {foo}` 12 | | | 13 | | required by a bound introduced by this call 14 | | 15 | = note: Consider using `#[axum::debug_handler]` to improve the error message 16 | = help: the following other types implement trait `Handler`: 17 | `Layered` implements `Handler` 18 | `MethodRouter` implements `Handler<(), S>` 19 | note: required by a bound in `axum::routing::get` 20 | --> $WORKSPACE/axum/src/routing/method_routing.rs 21 | | 22 | | top_level_handler_fn!(get, GET); 23 | | ^^^^^^^^^^^^^^^^^^^^^^---^^^^^^ 24 | | | | 25 | | | required by a bound in this function 26 | | required by this bound in `get` 27 | = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info) 28 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/generic_without_via_rejection.rs: -------------------------------------------------------------------------------- 1 | use axum::{routing::get, Router}; 2 | use axum_macros::FromRequest; 3 | 4 | #[derive(FromRequest, Clone)] 5 | #[from_request(rejection(Foo))] 6 | struct Extractor(T); 7 | 8 | async fn foo(_: Extractor<()>) {} 9 | 10 | fn main() { 11 | _ = Router::<()>::new().route("/", get(foo)); 12 | } 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/generic_without_via_rejection.stderr: -------------------------------------------------------------------------------- 1 | error: #[derive(FromRequest)] only supports generics when used with #[from_request(via)] 2 | --> tests/from_request/fail/generic_without_via_rejection.rs:6:18 3 | | 4 | 6 | struct Extractor(T); 5 | | ^ 6 | 7 | error[E0277]: the trait bound `fn(Extractor<()>) -> impl Future {foo}: Handler<_, _>` is not satisfied 8 | --> tests/from_request/fail/generic_without_via_rejection.rs:11:44 9 | | 10 | 11 | _ = Router::<()>::new().route("/", get(foo)); 11 | | --- ^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Extractor<()>) -> impl Future {foo}` 12 | | | 13 | | required by a bound introduced by this call 14 | | 15 | = note: Consider using `#[axum::debug_handler]` to improve the error message 16 | = help: the following other types implement trait `Handler`: 17 | `Layered` implements `Handler` 18 | `MethodRouter` implements `Handler<(), S>` 19 | note: required by a bound in `axum::routing::get` 20 | --> $WORKSPACE/axum/src/routing/method_routing.rs 21 | | 22 | | top_level_handler_fn!(get, GET); 23 | | ^^^^^^^^^^^^^^^^^^^^^^---^^^^^^ 24 | | | | 25 | | | required by a bound in this function 26 | | required by this bound in `get` 27 | = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info) 28 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/not_enum_or_struct.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | union Extractor {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/not_enum_or_struct.stderr: -------------------------------------------------------------------------------- 1 | error: expected `struct` or `enum` 2 | --> tests/from_request/fail/not_enum_or_struct.rs:4:1 3 | | 4 | 4 | union Extractor {} 5 | | ^^^^^^^^^^^^^^^^^^ 6 | 7 | error: unions cannot have zero fields 8 | --> tests/from_request/fail/not_enum_or_struct.rs:4:1 9 | | 10 | 4 | union Extractor {} 11 | | ^^^^^^^^^^^^^^^^^^ 12 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/override_rejection_on_enum_without_via.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::rejection::ExtensionRejection, 3 | response::{IntoResponse, Response}, 4 | routing::get, 5 | Router, 6 | }; 7 | use axum_macros::FromRequest; 8 | 9 | fn main() { 10 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 11 | } 12 | 13 | async fn handler(_: MyExtractor) {} 14 | 15 | async fn handler_result(_: Result) {} 16 | 17 | #[derive(FromRequest, Clone)] 18 | #[from_request(rejection(MyRejection))] 19 | enum MyExtractor {} 20 | 21 | struct MyRejection {} 22 | 23 | impl From for MyRejection { 24 | fn from(_: ExtensionRejection) -> Self { 25 | todo!() 26 | } 27 | } 28 | 29 | impl IntoResponse for MyRejection { 30 | fn into_response(self) -> Response { 31 | todo!() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/parts_extracting_body.rs: -------------------------------------------------------------------------------- 1 | use axum::{extract::FromRequestParts, response::Response}; 2 | 3 | #[derive(FromRequestParts)] 4 | struct Extractor { 5 | body: String, 6 | } 7 | 8 | fn assert_from_request() 9 | where 10 | Extractor: FromRequestParts<(), Rejection = Response>, 11 | { 12 | } 13 | 14 | fn main() {} 15 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/parts_extracting_body.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `String: FromRequestParts<_>` is not satisfied 2 | --> tests/from_request/fail/parts_extracting_body.rs:5:11 3 | | 4 | 5 | body: String, 5 | | ^^^^^^ the trait `FromRequestParts<_>` is not implemented for `String` 6 | | 7 | = note: Function argument is not a valid axum extractor. 8 | See `https://docs.rs/axum/0.8/axum/extract/index.html` for details 9 | = help: the following other types implement trait `FromRequestParts`: 10 | `()` implements `FromRequestParts` 11 | `(T1, T2)` implements `FromRequestParts` 12 | `(T1, T2, T3)` implements `FromRequestParts` 13 | `(T1, T2, T3, T4)` implements `FromRequestParts` 14 | `(T1, T2, T3, T4, T5)` implements `FromRequestParts` 15 | `(T1, T2, T3, T4, T5, T6)` implements `FromRequestParts` 16 | `(T1, T2, T3, T4, T5, T6, T7)` implements `FromRequestParts` 17 | `(T1, T2, T3, T4, T5, T6, T7, T8)` implements `FromRequestParts` 18 | and $N others 19 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/state_infer_multiple_different_types.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::State; 2 | use axum_macros::FromRequest; 3 | 4 | #[derive(FromRequest)] 5 | struct Extractor { 6 | inner_state: State, 7 | other_state: State, 8 | } 9 | 10 | #[derive(Clone)] 11 | struct AppState {} 12 | 13 | #[derive(Clone)] 14 | struct OtherState {} 15 | 16 | fn assert_from_request() 17 | where 18 | Extractor: axum::extract::FromRequest, 19 | { 20 | } 21 | 22 | fn main() {} 23 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/state_infer_multiple_different_types.stderr: -------------------------------------------------------------------------------- 1 | error: can't infer state type, please add `#[from_request(state = MyStateType)]` attribute 2 | --> tests/from_request/fail/state_infer_multiple_different_types.rs:4:10 3 | | 4 | 4 | #[derive(FromRequest)] 5 | | ^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `FromRequest` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/unknown_attr_container.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | #[from_request(foo)] 5 | struct Extractor; 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/unknown_attr_container.stderr: -------------------------------------------------------------------------------- 1 | error: expected one of: `via`, `rejection`, `state` 2 | --> tests/from_request/fail/unknown_attr_container.rs:4:16 3 | | 4 | 4 | #[from_request(foo)] 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/unknown_attr_field.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | struct Extractor(#[from_request(foo)] String); 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/unknown_attr_field.stderr: -------------------------------------------------------------------------------- 1 | error: expected `via` 2 | --> tests/from_request/fail/unknown_attr_field.rs:4:33 3 | | 4 | 4 | struct Extractor(#[from_request(foo)] String); 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/via_on_container_and_field.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | #[from_request(via(axum::Extension))] 5 | struct Extractor(#[from_request(via(axum::Extension))] State); 6 | 7 | #[derive(Clone)] 8 | struct State; 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/via_on_container_and_field.stderr: -------------------------------------------------------------------------------- 1 | error: `#[from_request(via(...))]` on a field cannot be used together with `#[from_request(...)]` on the container 2 | --> tests/from_request/fail/via_on_container_and_field.rs:5:33 3 | | 4 | 5 | struct Extractor(#[from_request(via(axum::Extension))] State); 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/container.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRequest, Json}, 3 | response::Response, 4 | }; 5 | use serde::Deserialize; 6 | 7 | #[derive(Deserialize, FromRequest)] 8 | #[from_request(via(Json))] 9 | struct Extractor { 10 | one: i32, 11 | two: String, 12 | three: bool, 13 | } 14 | 15 | fn assert_from_request() 16 | where 17 | Extractor: FromRequest<(), Rejection = Response>, 18 | { 19 | } 20 | 21 | fn main() {} 22 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/container_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{Extension, FromRequestParts}, 3 | response::Response, 4 | }; 5 | 6 | #[derive(Clone, FromRequestParts)] 7 | #[from_request(via(Extension))] 8 | struct Extractor { 9 | one: i32, 10 | two: String, 11 | three: bool, 12 | } 13 | 14 | fn assert_from_request() 15 | where 16 | Extractor: FromRequestParts<(), Rejection = Response>, 17 | { 18 | } 19 | 20 | fn main() {} 21 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/empty_named.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | struct Extractor {} 5 | 6 | fn assert_from_request() 7 | where 8 | Extractor: axum::extract::FromRequest<(), Rejection = std::convert::Infallible>, 9 | { 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/empty_named_parts.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequestParts; 2 | 3 | #[derive(FromRequestParts)] 4 | struct Extractor {} 5 | 6 | fn assert_from_request() 7 | where 8 | Extractor: axum::extract::FromRequestParts<(), Rejection = std::convert::Infallible>, 9 | { 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/empty_tuple.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | struct Extractor(); 5 | 6 | fn assert_from_request() 7 | where 8 | Extractor: axum::extract::FromRequest<(), Rejection = std::convert::Infallible>, 9 | { 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/empty_tuple_parts.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequestParts; 2 | 3 | #[derive(FromRequestParts)] 4 | struct Extractor(); 5 | 6 | fn assert_from_request() 7 | where 8 | Extractor: axum::extract::FromRequestParts<(), Rejection = std::convert::Infallible>, 9 | { 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/enum_via.rs: -------------------------------------------------------------------------------- 1 | use axum::{routing::get, Extension, Router}; 2 | use axum_macros::FromRequest; 3 | 4 | #[derive(FromRequest, Clone)] 5 | #[from_request(via(Extension))] 6 | enum Extractor {} 7 | 8 | async fn foo(_: Extractor) {} 9 | 10 | fn main() { 11 | _ = Router::<()>::new().route("/", get(foo)); 12 | } 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/enum_via_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{routing::get, Extension, Router}; 2 | use axum_macros::FromRequestParts; 3 | 4 | #[derive(FromRequestParts, Clone)] 5 | #[from_request(via(Extension))] 6 | enum Extractor {} 7 | 8 | async fn foo(_: Extractor) {} 9 | 10 | fn main() { 11 | _ = Router::<()>::new().route("/", get(foo)); 12 | } 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/named.rs: -------------------------------------------------------------------------------- 1 | use axum::{extract::FromRequest, response::Response}; 2 | use axum_extra::{ 3 | headers::{self, UserAgent}, 4 | typed_header::TypedHeaderRejection, 5 | TypedHeader, 6 | }; 7 | 8 | #[derive(FromRequest)] 9 | struct Extractor { 10 | uri: axum::http::Uri, 11 | user_agent: TypedHeader, 12 | content_type: TypedHeader, 13 | etag: Option>, 14 | host: Result, TypedHeaderRejection>, 15 | body: String, 16 | } 17 | 18 | fn assert_from_request() 19 | where 20 | Extractor: FromRequest<(), Rejection = Response>, 21 | { 22 | } 23 | 24 | fn main() {} 25 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/named_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{extract::FromRequestParts, response::Response}; 2 | use axum_extra::{ 3 | headers::{self, UserAgent}, 4 | typed_header::TypedHeaderRejection, 5 | TypedHeader, 6 | }; 7 | 8 | #[derive(FromRequestParts)] 9 | struct Extractor { 10 | uri: axum::http::Uri, 11 | user_agent: TypedHeader, 12 | content_type: TypedHeader, 13 | etag: Option>, 14 | host: Result, TypedHeaderRejection>, 15 | } 16 | 17 | fn assert_from_request() 18 | where 19 | Extractor: FromRequestParts<(), Rejection = Response>, 20 | { 21 | } 22 | 23 | fn main() {} 24 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/named_via.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{Extension, FromRequest}, 3 | response::Response, 4 | }; 5 | use axum_extra::{ 6 | headers::{self, UserAgent}, 7 | typed_header::TypedHeaderRejection, 8 | TypedHeader, 9 | }; 10 | 11 | #[derive(FromRequest)] 12 | struct Extractor { 13 | #[from_request(via(Extension))] 14 | state: State, 15 | #[from_request(via(TypedHeader))] 16 | user_agent: UserAgent, 17 | #[from_request(via(TypedHeader))] 18 | content_type: headers::ContentType, 19 | #[from_request(via(TypedHeader))] 20 | etag: Option, 21 | #[from_request(via(TypedHeader))] 22 | host: Result, 23 | } 24 | 25 | fn assert_from_request() 26 | where 27 | Extractor: FromRequest<(), Rejection = Response>, 28 | { 29 | } 30 | 31 | #[derive(Clone)] 32 | struct State; 33 | 34 | fn main() {} 35 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/named_via_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{Extension, FromRequestParts}, 3 | response::Response, 4 | }; 5 | use axum_extra::{ 6 | headers::{self, UserAgent}, 7 | typed_header::TypedHeaderRejection, 8 | TypedHeader, 9 | }; 10 | 11 | #[derive(FromRequestParts)] 12 | struct Extractor { 13 | #[from_request(via(Extension))] 14 | state: State, 15 | #[from_request(via(TypedHeader))] 16 | user_agent: UserAgent, 17 | #[from_request(via(TypedHeader))] 18 | content_type: headers::ContentType, 19 | #[from_request(via(TypedHeader))] 20 | etag: Option, 21 | #[from_request(via(TypedHeader))] 22 | host: Result, 23 | } 24 | 25 | fn assert_from_request() 26 | where 27 | Extractor: FromRequestParts<(), Rejection = Response>, 28 | { 29 | } 30 | 31 | #[derive(Clone)] 32 | struct State; 33 | 34 | fn main() {} 35 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/override_rejection.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{rejection::ExtensionRejection, FromRequest, Request}, 3 | http::StatusCode, 4 | response::{IntoResponse, Response}, 5 | routing::get, 6 | Extension, Router, 7 | }; 8 | 9 | fn main() { 10 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 11 | } 12 | 13 | async fn handler(_: MyExtractor) {} 14 | 15 | async fn handler_result(_: Result) {} 16 | 17 | #[derive(FromRequest)] 18 | #[from_request(rejection(MyRejection))] 19 | struct MyExtractor { 20 | one: Extension, 21 | #[from_request(via(Extension))] 22 | two: String, 23 | three: OtherExtractor, 24 | } 25 | 26 | struct OtherExtractor; 27 | 28 | impl FromRequest for OtherExtractor 29 | where 30 | S: Send + Sync, 31 | { 32 | // this rejection doesn't implement `Display` and `Error` 33 | type Rejection = (StatusCode, String); 34 | 35 | async fn from_request(_req: Request, _state: &S) -> Result { 36 | todo!() 37 | } 38 | } 39 | 40 | struct MyRejection {} 41 | 42 | impl From for MyRejection { 43 | fn from(_: ExtensionRejection) -> Self { 44 | todo!() 45 | } 46 | } 47 | 48 | impl From<(StatusCode, String)> for MyRejection { 49 | fn from(_: (StatusCode, String)) -> Self { 50 | todo!() 51 | } 52 | } 53 | 54 | impl IntoResponse for MyRejection { 55 | fn into_response(self) -> Response { 56 | todo!() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/override_rejection_non_generic.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::rejection::JsonRejection, 3 | response::{IntoResponse, Response}, 4 | routing::get, 5 | Router, 6 | }; 7 | use axum_macros::FromRequest; 8 | use serde::Deserialize; 9 | use std::collections::HashMap; 10 | 11 | fn main() { 12 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 13 | } 14 | 15 | async fn handler(_: MyJson) {} 16 | 17 | async fn handler_result(_: Result) {} 18 | 19 | #[derive(FromRequest, Deserialize)] 20 | #[from_request(via(axum::extract::Json), rejection(MyJsonRejection))] 21 | #[serde(transparent)] 22 | struct MyJson(HashMap); 23 | 24 | struct MyJsonRejection {} 25 | 26 | impl From for MyJsonRejection { 27 | fn from(_: JsonRejection) -> Self { 28 | todo!() 29 | } 30 | } 31 | 32 | impl IntoResponse for MyJsonRejection { 33 | fn into_response(self) -> Response { 34 | todo!() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/override_rejection_non_generic_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::rejection::QueryRejection, 3 | response::{IntoResponse, Response}, 4 | routing::get, 5 | Router, 6 | }; 7 | use axum_macros::FromRequestParts; 8 | use serde::Deserialize; 9 | use std::collections::HashMap; 10 | 11 | fn main() { 12 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 13 | } 14 | 15 | async fn handler(_: MyQuery) {} 16 | 17 | async fn handler_result(_: Result) {} 18 | 19 | #[derive(FromRequestParts, Deserialize)] 20 | #[from_request(via(axum::extract::Query), rejection(MyQueryRejection))] 21 | #[serde(transparent)] 22 | struct MyQuery(HashMap); 23 | 24 | struct MyQueryRejection {} 25 | 26 | impl From for MyQueryRejection { 27 | fn from(_: QueryRejection) -> Self { 28 | todo!() 29 | } 30 | } 31 | 32 | impl IntoResponse for MyQueryRejection { 33 | fn into_response(self) -> Response { 34 | todo!() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/override_rejection_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{rejection::ExtensionRejection, FromRequestParts}, 3 | http::{request::Parts, StatusCode}, 4 | response::{IntoResponse, Response}, 5 | routing::get, 6 | Extension, Router, 7 | }; 8 | 9 | fn main() { 10 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 11 | } 12 | 13 | async fn handler(_: MyExtractor) {} 14 | 15 | async fn handler_result(_: Result) {} 16 | 17 | #[derive(FromRequestParts)] 18 | #[from_request(rejection(MyRejection))] 19 | struct MyExtractor { 20 | one: Extension, 21 | #[from_request(via(Extension))] 22 | two: String, 23 | three: OtherExtractor, 24 | } 25 | 26 | struct OtherExtractor; 27 | 28 | impl FromRequestParts for OtherExtractor 29 | where 30 | S: Send + Sync, 31 | { 32 | // this rejection doesn't implement `Display` and `Error` 33 | type Rejection = (StatusCode, String); 34 | 35 | async fn from_request_parts(_parts: &mut Parts, _state: &S) -> Result { 36 | todo!() 37 | } 38 | } 39 | 40 | struct MyRejection {} 41 | 42 | impl From for MyRejection { 43 | fn from(_: ExtensionRejection) -> Self { 44 | todo!() 45 | } 46 | } 47 | 48 | impl From<(StatusCode, String)> for MyRejection { 49 | fn from(_: (StatusCode, String)) -> Self { 50 | todo!() 51 | } 52 | } 53 | 54 | impl IntoResponse for MyRejection { 55 | fn into_response(self) -> Response { 56 | todo!() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/override_rejection_with_via_on_enum.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::rejection::ExtensionRejection, 3 | response::{IntoResponse, Response}, 4 | routing::get, 5 | Router, 6 | }; 7 | use axum_macros::FromRequest; 8 | 9 | fn main() { 10 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 11 | } 12 | 13 | async fn handler(_: MyExtractor) {} 14 | 15 | async fn handler_result(_: Result) {} 16 | 17 | #[derive(FromRequest, Clone)] 18 | #[from_request(via(axum::Extension), rejection(MyRejection))] 19 | enum MyExtractor {} 20 | 21 | struct MyRejection {} 22 | 23 | impl From for MyRejection { 24 | fn from(_: ExtensionRejection) -> Self { 25 | todo!() 26 | } 27 | } 28 | 29 | impl IntoResponse for MyRejection { 30 | fn into_response(self) -> Response { 31 | todo!() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/override_rejection_with_via_on_enum_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::rejection::ExtensionRejection, 3 | response::{IntoResponse, Response}, 4 | routing::get, 5 | Router, 6 | }; 7 | use axum_macros::FromRequestParts; 8 | 9 | fn main() { 10 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 11 | } 12 | 13 | async fn handler(_: MyExtractor) {} 14 | 15 | async fn handler_result(_: Result) {} 16 | 17 | #[derive(FromRequestParts, Clone)] 18 | #[from_request(via(axum::Extension), rejection(MyRejection))] 19 | enum MyExtractor {} 20 | 21 | struct MyRejection {} 22 | 23 | impl From for MyRejection { 24 | fn from(_: ExtensionRejection) -> Self { 25 | todo!() 26 | } 27 | } 28 | 29 | impl IntoResponse for MyRejection { 30 | fn into_response(self) -> Response { 31 | todo!() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/override_rejection_with_via_on_struct.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::rejection::JsonRejection, 3 | response::{IntoResponse, Response}, 4 | routing::get, 5 | Router, 6 | }; 7 | use axum_macros::FromRequest; 8 | use serde::Deserialize; 9 | 10 | fn main() { 11 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 12 | } 13 | 14 | #[derive(Deserialize)] 15 | struct Payload {} 16 | 17 | async fn handler(_: MyJson) {} 18 | 19 | async fn handler_result(_: Result, MyJsonRejection>) {} 20 | 21 | #[derive(FromRequest)] 22 | #[from_request(via(axum::Json), rejection(MyJsonRejection))] 23 | struct MyJson(T); 24 | 25 | struct MyJsonRejection {} 26 | 27 | impl From for MyJsonRejection { 28 | fn from(_: JsonRejection) -> Self { 29 | todo!() 30 | } 31 | } 32 | 33 | impl IntoResponse for MyJsonRejection { 34 | fn into_response(self) -> Response { 35 | todo!() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/override_rejection_with_via_on_struct_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::rejection::QueryRejection, 3 | response::{IntoResponse, Response}, 4 | routing::get, 5 | Router, 6 | }; 7 | use axum_macros::FromRequestParts; 8 | use serde::Deserialize; 9 | 10 | fn main() { 11 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 12 | } 13 | 14 | #[derive(Deserialize)] 15 | struct Payload {} 16 | 17 | async fn handler(_: MyQuery) {} 18 | 19 | async fn handler_result(_: Result, MyQueryRejection>) {} 20 | 21 | #[derive(FromRequestParts)] 22 | #[from_request(via(axum::extract::Query), rejection(MyQueryRejection))] 23 | struct MyQuery(T); 24 | 25 | struct MyQueryRejection {} 26 | 27 | impl From for MyQueryRejection { 28 | fn from(_: QueryRejection) -> Self { 29 | todo!() 30 | } 31 | } 32 | 33 | impl IntoResponse for MyQueryRejection { 34 | fn into_response(self) -> Response { 35 | todo!() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_cookie.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::FromRef; 2 | use axum_extra::extract::cookie::{Key, PrivateCookieJar}; 3 | use axum_macros::FromRequest; 4 | 5 | #[derive(FromRequest)] 6 | #[from_request(state(AppState))] 7 | struct Extractor { 8 | cookies: PrivateCookieJar, 9 | } 10 | 11 | struct AppState { 12 | key: Key, 13 | } 14 | 15 | impl FromRef for Key { 16 | fn from_ref(input: &AppState) -> Self { 17 | input.key.clone() 18 | } 19 | } 20 | 21 | fn assert_from_request() 22 | where 23 | Extractor: axum::extract::FromRequest, 24 | { 25 | } 26 | 27 | fn main() {} 28 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_enum_via.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRef, State}, 3 | routing::get, 4 | Router, 5 | }; 6 | use axum_macros::FromRequest; 7 | 8 | fn main() { 9 | let _: axum::Router = Router::new() 10 | .route("/a", get(|_: AppState| async {})) 11 | .route("/b", get(|_: InnerState| async {})) 12 | .with_state(AppState::default()); 13 | } 14 | 15 | #[derive(Clone, FromRequest)] 16 | #[from_request(via(State))] 17 | enum AppState { 18 | One, 19 | } 20 | 21 | impl Default for AppState { 22 | fn default() -> AppState { 23 | Self::One 24 | } 25 | } 26 | 27 | #[derive(FromRequest)] 28 | #[from_request(via(State), state(AppState))] 29 | enum InnerState {} 30 | 31 | impl FromRef for InnerState { 32 | fn from_ref(_: &AppState) -> Self { 33 | todo!("🤷") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_enum_via_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRef, State}, 3 | routing::get, 4 | Router, 5 | }; 6 | use axum_macros::FromRequestParts; 7 | 8 | fn main() { 9 | let _: axum::Router = Router::new() 10 | .route("/a", get(|_: AppState| async {})) 11 | .route("/b", get(|_: InnerState| async {})) 12 | .route("/c", get(|_: AppState, _: InnerState| async {})) 13 | .with_state(AppState::default()); 14 | } 15 | 16 | #[derive(Clone, FromRequestParts)] 17 | #[from_request(via(State))] 18 | enum AppState { 19 | One, 20 | } 21 | 22 | impl Default for AppState { 23 | fn default() -> AppState { 24 | Self::One 25 | } 26 | } 27 | 28 | #[derive(FromRequestParts)] 29 | #[from_request(via(State), state(AppState))] 30 | enum InnerState {} 31 | 32 | impl FromRef for InnerState { 33 | fn from_ref(_: &AppState) -> Self { 34 | todo!("🤷") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_explicit.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRef, State}, 3 | routing::get, 4 | Router, 5 | }; 6 | use axum_macros::FromRequest; 7 | 8 | fn main() { 9 | let _: axum::Router = Router::new() 10 | .route("/b", get(|_: Extractor| async {})) 11 | .with_state(AppState::default()); 12 | } 13 | 14 | #[derive(FromRequest)] 15 | #[from_request(state(AppState))] 16 | struct Extractor { 17 | app_state: State, 18 | one: State, 19 | two: State, 20 | other_extractor: String, 21 | } 22 | 23 | #[derive(Clone, Default)] 24 | struct AppState { 25 | one: One, 26 | two: Two, 27 | } 28 | 29 | #[derive(Clone, Default)] 30 | struct One {} 31 | 32 | impl FromRef for One { 33 | fn from_ref(input: &AppState) -> Self { 34 | input.one.clone() 35 | } 36 | } 37 | 38 | #[derive(Clone, Default)] 39 | struct Two {} 40 | 41 | impl FromRef for Two { 42 | fn from_ref(input: &AppState) -> Self { 43 | input.two.clone() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_explicit_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRef, Query, State}, 3 | routing::get, 4 | Router, 5 | }; 6 | use axum_macros::FromRequestParts; 7 | use std::collections::HashMap; 8 | 9 | fn main() { 10 | let _: axum::Router = Router::new() 11 | .route("/b", get(|_: Extractor| async {})) 12 | .with_state(AppState::default()); 13 | } 14 | 15 | #[derive(FromRequestParts)] 16 | #[from_request(state(AppState))] 17 | struct Extractor { 18 | inner_state: State, 19 | other: Query>, 20 | } 21 | 22 | #[derive(Default, Clone)] 23 | struct AppState { 24 | inner: InnerState, 25 | } 26 | 27 | #[derive(Clone, Default)] 28 | struct InnerState {} 29 | 30 | impl FromRef for InnerState { 31 | fn from_ref(input: &AppState) -> Self { 32 | input.inner.clone() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_field_explicit.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRef, State}, 3 | routing::get, 4 | Router, 5 | }; 6 | use axum_macros::FromRequest; 7 | 8 | fn main() { 9 | let _: axum::Router = Router::new() 10 | .route("/", get(|_: Extractor| async {})) 11 | .with_state(AppState::default()); 12 | } 13 | 14 | #[derive(FromRequest)] 15 | #[from_request(state(AppState))] 16 | struct Extractor { 17 | #[from_request(via(State))] 18 | state: AppState, 19 | #[from_request(via(State))] 20 | inner: InnerState, 21 | } 22 | 23 | #[derive(Clone, Default)] 24 | struct AppState { 25 | inner: InnerState, 26 | } 27 | 28 | #[derive(Clone, Default)] 29 | struct InnerState {} 30 | 31 | impl FromRef for InnerState { 32 | fn from_ref(input: &AppState) -> Self { 33 | input.inner.clone() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_field_infer.rs: -------------------------------------------------------------------------------- 1 | use axum::{extract::State, routing::get, Router}; 2 | use axum_macros::FromRequest; 3 | 4 | fn main() { 5 | let _: axum::Router = Router::new() 6 | .route("/", get(|_: Extractor| async {})) 7 | .with_state(AppState::default()); 8 | } 9 | 10 | #[derive(FromRequest)] 11 | struct Extractor { 12 | #[from_request(via(State))] 13 | state: AppState, 14 | } 15 | 16 | #[derive(Clone, Default)] 17 | struct AppState {} 18 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_infer.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::State; 2 | use axum_macros::FromRequest; 3 | 4 | #[derive(FromRequest)] 5 | struct Extractor { 6 | inner_state: State, 7 | } 8 | 9 | #[derive(Clone)] 10 | struct AppState {} 11 | 12 | fn assert_from_request() 13 | where 14 | Extractor: axum::extract::FromRequest, 15 | { 16 | } 17 | 18 | fn main() {} 19 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_infer_multiple.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::State; 2 | use axum_macros::FromRequest; 3 | 4 | #[derive(FromRequest)] 5 | struct Extractor { 6 | inner_state: State, 7 | also_inner_state: State, 8 | } 9 | 10 | #[derive(Clone)] 11 | struct AppState {} 12 | 13 | fn assert_from_request() 14 | where 15 | Extractor: axum::extract::FromRequest, 16 | { 17 | } 18 | 19 | fn main() {} 20 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_infer_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::State; 2 | use axum_macros::FromRequestParts; 3 | 4 | #[derive(FromRequestParts)] 5 | struct Extractor { 6 | inner_state: State, 7 | } 8 | 9 | #[derive(Clone)] 10 | struct AppState {} 11 | 12 | fn assert_from_request() 13 | where 14 | Extractor: axum::extract::FromRequestParts, 15 | { 16 | } 17 | 18 | fn main() {} 19 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_via.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRef, State}, 3 | routing::get, 4 | Router, 5 | }; 6 | use axum_macros::FromRequest; 7 | 8 | fn main() { 9 | let _: axum::Router = Router::new() 10 | .route("/b", get(|_: (), _: AppState| async {})) 11 | .route("/c", get(|_: (), _: InnerState| async {})) 12 | .with_state(AppState::default()); 13 | } 14 | 15 | #[derive(Clone, Default, FromRequest)] 16 | #[from_request(via(State), state(AppState))] 17 | struct AppState { 18 | inner: InnerState, 19 | } 20 | 21 | #[derive(Clone, Default, FromRequest)] 22 | #[from_request(via(State), state(AppState))] 23 | struct InnerState {} 24 | 25 | impl FromRef for InnerState { 26 | fn from_ref(input: &AppState) -> Self { 27 | input.inner.clone() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_via_infer.rs: -------------------------------------------------------------------------------- 1 | use axum::{extract::State, routing::get, Router}; 2 | use axum_macros::FromRequest; 3 | 4 | fn main() { 5 | let _: axum::Router = Router::new() 6 | .route("/b", get(|_: AppState| async {})) 7 | .with_state(AppState::default()); 8 | } 9 | 10 | // if we're extract "via" `State` and not specifying state 11 | // assume `AppState` is the state 12 | #[derive(Clone, Default, FromRequest)] 13 | #[from_request(via(State))] 14 | struct AppState {} 15 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_via_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRef, State}, 3 | routing::get, 4 | Router, 5 | }; 6 | use axum_macros::FromRequestParts; 7 | 8 | fn main() { 9 | let _: axum::Router = Router::new() 10 | .route("/a", get(|_: AppState, _: InnerState, _: String| async {})) 11 | .route("/b", get(|_: AppState, _: String| async {})) 12 | .route("/c", get(|_: InnerState, _: String| async {})) 13 | .with_state(AppState::default()); 14 | } 15 | 16 | #[derive(Clone, Default, FromRequestParts)] 17 | #[from_request(via(State))] 18 | struct AppState { 19 | inner: InnerState, 20 | } 21 | 22 | #[derive(Clone, Default, FromRequestParts)] 23 | #[from_request(via(State), state(AppState))] 24 | struct InnerState {} 25 | 26 | impl FromRef for InnerState { 27 | fn from_ref(input: &AppState) -> Self { 28 | input.inner.clone() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_with_rejection.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::State, 3 | response::{IntoResponse, Response}, 4 | routing::get, 5 | Router, 6 | }; 7 | use axum_macros::FromRequest; 8 | use std::convert::Infallible; 9 | 10 | fn main() { 11 | let _: axum::Router = Router::new() 12 | .route("/a", get(|_: Extractor| async {})) 13 | .with_state(AppState::default()); 14 | } 15 | 16 | #[derive(Clone, Default, FromRequest)] 17 | #[from_request(rejection(MyRejection))] 18 | struct Extractor { 19 | state: State, 20 | } 21 | 22 | #[derive(Clone, Default)] 23 | struct AppState {} 24 | 25 | struct MyRejection {} 26 | 27 | impl From for MyRejection { 28 | fn from(err: Infallible) -> Self { 29 | match err {} 30 | } 31 | } 32 | 33 | impl IntoResponse for MyRejection { 34 | fn into_response(self) -> Response { 35 | ().into_response() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/tuple.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | struct Extractor(axum::http::HeaderMap, String); 5 | 6 | fn assert_from_request() 7 | where 8 | Extractor: axum::extract::FromRequest<()>, 9 | { 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/tuple_parts.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequestParts; 2 | 3 | #[derive(FromRequestParts)] 4 | struct Extractor(axum::http::HeaderMap, axum::http::Method); 5 | 6 | fn assert_from_request() 7 | where 8 | Extractor: axum::extract::FromRequestParts<()>, 9 | { 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/tuple_same_type_twice.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::Query; 2 | use axum_macros::FromRequest; 3 | use serde::Deserialize; 4 | 5 | #[derive(FromRequest)] 6 | struct Extractor(Query, axum::extract::Json); 7 | 8 | #[derive(Deserialize)] 9 | struct Payload {} 10 | 11 | fn assert_from_request() 12 | where 13 | Extractor: axum::extract::FromRequest<()>, 14 | { 15 | } 16 | 17 | fn main() {} 18 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/tuple_same_type_twice_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::Query; 2 | use axum_macros::FromRequestParts; 3 | use serde::Deserialize; 4 | 5 | #[derive(FromRequestParts)] 6 | struct Extractor(Query, axum::extract::Path); 7 | 8 | #[derive(Deserialize)] 9 | struct Payload {} 10 | 11 | fn assert_from_request() 12 | where 13 | Extractor: axum::extract::FromRequestParts<()>, 14 | { 15 | } 16 | 17 | fn main() {} 18 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/tuple_same_type_twice_via.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::Query; 2 | use axum::response::Response; 3 | use axum_macros::FromRequest; 4 | use serde::Deserialize; 5 | 6 | #[derive(FromRequest)] 7 | struct Extractor( 8 | #[from_request(via(Query))] Payload, 9 | #[from_request(via(axum::extract::Json))] Payload, 10 | ); 11 | 12 | #[derive(Deserialize)] 13 | struct Payload {} 14 | 15 | fn assert_from_request() 16 | where 17 | Extractor: axum::extract::FromRequest<(), Rejection = Response>, 18 | { 19 | } 20 | 21 | fn main() {} 22 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/tuple_same_type_twice_via_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::Query; 2 | use axum::response::Response; 3 | use axum_macros::FromRequestParts; 4 | use serde::Deserialize; 5 | 6 | #[derive(FromRequestParts)] 7 | struct Extractor( 8 | #[from_request(via(Query))] Payload, 9 | #[from_request(via(axum::extract::Path))] Payload, 10 | ); 11 | 12 | #[derive(Deserialize)] 13 | struct Payload {} 14 | 15 | fn assert_from_request() 16 | where 17 | Extractor: axum::extract::FromRequestParts<(), Rejection = Response>, 18 | { 19 | } 20 | 21 | fn main() {} 22 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/tuple_via.rs: -------------------------------------------------------------------------------- 1 | use axum::Extension; 2 | use axum_macros::FromRequest; 3 | 4 | #[derive(FromRequest)] 5 | struct Extractor(#[from_request(via(Extension))] State); 6 | 7 | #[derive(Clone)] 8 | struct State; 9 | 10 | fn assert_from_request() 11 | where 12 | Extractor: axum::extract::FromRequest<()>, 13 | { 14 | } 15 | 16 | fn main() {} 17 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/tuple_via_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::Extension; 2 | use axum_macros::FromRequestParts; 3 | 4 | #[derive(FromRequestParts)] 5 | struct Extractor(#[from_request(via(Extension))] State); 6 | 7 | #[derive(Clone)] 8 | struct State; 9 | 10 | fn assert_from_request() 11 | where 12 | Extractor: axum::extract::FromRequestParts<()>, 13 | { 14 | } 15 | 16 | fn main() {} 17 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/unit.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | struct Extractor; 5 | 6 | fn assert_from_request() 7 | where 8 | Extractor: axum::extract::FromRequest<(), Rejection = std::convert::Infallible>, 9 | { 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/unit_parts.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequestParts; 2 | 3 | #[derive(FromRequestParts)] 4 | struct Extractor; 5 | 6 | fn assert_from_request() 7 | where 8 | Extractor: axum::extract::FromRequestParts<(), Rejection = std::convert::Infallible>, 9 | { 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/missing_capture.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::TypedPath; 2 | use serde::Deserialize; 3 | 4 | #[derive(TypedPath, Deserialize)] 5 | #[typed_path("/users")] 6 | struct MyPath { 7 | id: u32, 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/missing_capture.stderr: -------------------------------------------------------------------------------- 1 | error[E0027]: pattern does not mention field `id` 2 | --> tests/typed_path/fail/missing_capture.rs:5:14 3 | | 4 | 5 | #[typed_path("/users")] 5 | | ^^^^^^^^ missing field `id` 6 | | 7 | help: include the missing field in the pattern 8 | | 9 | 5 | #[typed_path("/users" { id })] 10 | | ++++++ 11 | help: if you don't care about this missing field, you can explicitly ignore it 12 | | 13 | 5 | #[typed_path("/users" { .. })] 14 | | ++++++ 15 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/missing_field.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::TypedPath; 2 | use serde::Deserialize; 3 | 4 | #[derive(TypedPath, Deserialize)] 5 | #[typed_path("/users/{id}")] 6 | struct MyPath {} 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/missing_field.stderr: -------------------------------------------------------------------------------- 1 | error[E0026]: struct `MyPath` does not have a field named `id` 2 | --> tests/typed_path/fail/missing_field.rs:5:14 3 | | 4 | 5 | #[typed_path("/users/{id}")] 5 | | ^^^^^^^^^^^^^ struct `MyPath` does not have this field 6 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/not_deserialize.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::TypedPath; 2 | 3 | #[derive(TypedPath)] 4 | #[typed_path("/users/{id}")] 5 | struct MyPath { 6 | id: u32, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/route_not_starting_with_slash.rs: -------------------------------------------------------------------------------- 1 | use axum_extra::routing::TypedPath; 2 | 3 | #[derive(TypedPath)] 4 | #[typed_path("")] 5 | struct MyPath; 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/route_not_starting_with_slash.stderr: -------------------------------------------------------------------------------- 1 | error: paths must start with a `/`. Use "/" for root routes 2 | --> tests/typed_path/fail/route_not_starting_with_slash.rs:4:14 3 | | 4 | 4 | #[typed_path("")] 5 | | ^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/route_not_starting_with_slash_non_empty.rs: -------------------------------------------------------------------------------- 1 | use axum_extra::routing::TypedPath; 2 | 3 | #[derive(TypedPath)] 4 | #[typed_path("{foo}")] 5 | struct MyPath; 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/route_not_starting_with_slash_non_empty.stderr: -------------------------------------------------------------------------------- 1 | error: paths must start with a `/` 2 | --> tests/typed_path/fail/route_not_starting_with_slash_non_empty.rs:4:14 3 | | 4 | 4 | #[typed_path("{foo}")] 5 | | ^^^^^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/unit_with_capture.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::TypedPath; 2 | use serde::Deserialize; 3 | 4 | #[derive(TypedPath, Deserialize)] 5 | #[typed_path("/users/{id}")] 6 | struct MyPath; 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/unit_with_capture.stderr: -------------------------------------------------------------------------------- 1 | error: Typed paths for unit structs cannot contain captures 2 | --> tests/typed_path/fail/unit_with_capture.rs:5:14 3 | | 4 | 5 | #[typed_path("/users/{id}")] 5 | | ^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/pass/customize_rejection.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::rejection::PathRejection, 3 | response::{IntoResponse, Response}, 4 | }; 5 | use axum_extra::routing::{RouterExt, TypedPath}; 6 | use serde::Deserialize; 7 | 8 | #[derive(TypedPath, Deserialize)] 9 | #[typed_path("/{foo}", rejection(MyRejection))] 10 | struct MyPathNamed { 11 | foo: String, 12 | } 13 | 14 | #[derive(TypedPath, Deserialize)] 15 | #[typed_path("/", rejection(MyRejection))] 16 | struct MyPathUnit; 17 | 18 | #[derive(TypedPath, Deserialize)] 19 | #[typed_path("/{foo}", rejection(MyRejection))] 20 | struct MyPathUnnamed(String); 21 | 22 | struct MyRejection; 23 | 24 | impl IntoResponse for MyRejection { 25 | fn into_response(self) -> Response { 26 | ().into_response() 27 | } 28 | } 29 | 30 | impl From for MyRejection { 31 | fn from(_: PathRejection) -> Self { 32 | Self 33 | } 34 | } 35 | 36 | impl Default for MyRejection { 37 | fn default() -> Self { 38 | Self 39 | } 40 | } 41 | 42 | fn main() { 43 | _ = axum::Router::<()>::new() 44 | .typed_get(|_: Result| async {}) 45 | .typed_post(|_: Result| async {}) 46 | .typed_put(|_: Result| async {}); 47 | } 48 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/pass/into_uri.rs: -------------------------------------------------------------------------------- 1 | use axum::http::Uri; 2 | use axum_extra::routing::TypedPath; 3 | use serde::Deserialize; 4 | 5 | #[derive(TypedPath, Deserialize)] 6 | #[typed_path("/{id}")] 7 | struct Named { 8 | id: u32, 9 | } 10 | 11 | #[derive(TypedPath, Deserialize)] 12 | #[typed_path("/{id}")] 13 | struct Unnamed(u32); 14 | 15 | #[derive(TypedPath, Deserialize)] 16 | #[typed_path("/")] 17 | struct Unit; 18 | 19 | fn main() { 20 | let _: Uri = Named { id: 1 }.to_uri(); 21 | let _: Uri = Unnamed(1).to_uri(); 22 | let _: Uri = Unit.to_uri(); 23 | } 24 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/pass/named_fields_struct.rs: -------------------------------------------------------------------------------- 1 | use axum_extra::routing::TypedPath; 2 | use serde::Deserialize; 3 | 4 | #[derive(TypedPath, Deserialize)] 5 | #[typed_path("/users/{user_id}/teams/{team_id}")] 6 | struct MyPath { 7 | user_id: u32, 8 | team_id: u32, 9 | } 10 | 11 | fn main() { 12 | _ = axum::Router::<()>::new().route("/", axum::routing::get(|_: MyPath| async {})); 13 | 14 | assert_eq!(MyPath::PATH, "/users/{user_id}/teams/{team_id}"); 15 | assert_eq!( 16 | format!( 17 | "{}", 18 | MyPath { 19 | user_id: 1, 20 | team_id: 2 21 | } 22 | ), 23 | "/users/1/teams/2" 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/pass/result_handler.rs: -------------------------------------------------------------------------------- 1 | use axum::{extract::rejection::PathRejection, http::StatusCode}; 2 | use axum_extra::routing::{RouterExt, TypedPath}; 3 | use serde::Deserialize; 4 | 5 | #[derive(TypedPath, Deserialize)] 6 | #[typed_path("/users/{id}")] 7 | struct UsersShow { 8 | id: String, 9 | } 10 | 11 | async fn result_handler(_: Result) {} 12 | 13 | #[derive(TypedPath, Deserialize)] 14 | #[typed_path("/users")] 15 | struct UsersIndex; 16 | 17 | async fn result_handler_unit_struct(_: Result) {} 18 | 19 | fn main() { 20 | _ = axum::Router::<()>::new() 21 | .typed_post(result_handler) 22 | .typed_post(result_handler_unit_struct); 23 | } 24 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/pass/tuple_struct.rs: -------------------------------------------------------------------------------- 1 | use axum_extra::routing::TypedPath; 2 | use serde::Deserialize; 3 | 4 | pub type Result = std::result::Result; 5 | 6 | #[derive(TypedPath, Deserialize)] 7 | #[typed_path("/users/{user_id}/teams/{team_id}")] 8 | struct MyPath(u32, u32); 9 | 10 | fn main() { 11 | _ = axum::Router::<()>::new().route("/", axum::routing::get(|_: MyPath| async {})); 12 | 13 | assert_eq!(MyPath::PATH, "/users/{user_id}/teams/{team_id}"); 14 | assert_eq!(format!("{}", MyPath(1, 2)), "/users/1/teams/2"); 15 | } 16 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/pass/unit_struct.rs: -------------------------------------------------------------------------------- 1 | use axum_extra::routing::TypedPath; 2 | 3 | #[derive(TypedPath)] 4 | #[typed_path("/users")] 5 | struct MyPath; 6 | 7 | fn main() { 8 | _ = axum::Router::<()>::new().route("/", axum::routing::get(|_: MyPath| async {})); 9 | 10 | assert_eq!(MyPath::PATH, "/users"); 11 | assert_eq!(format!("{}", MyPath), "/users"); 12 | } 13 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/pass/url_encoding.rs: -------------------------------------------------------------------------------- 1 | use axum_extra::routing::TypedPath; 2 | use serde::Deserialize; 3 | 4 | #[derive(TypedPath, Deserialize)] 5 | #[typed_path("/{param}")] 6 | struct Named { 7 | param: String, 8 | } 9 | 10 | #[derive(TypedPath, Deserialize)] 11 | #[typed_path("/{param}")] 12 | struct Unnamed(String); 13 | 14 | fn main() { 15 | assert_eq!( 16 | format!( 17 | "{}", 18 | Named { 19 | param: "a b".to_string() 20 | } 21 | ), 22 | "/a%20b" 23 | ); 24 | 25 | assert_eq!(format!("{}", Unnamed("a b".to_string()),), "/a%20b"); 26 | } 27 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/pass/wildcards.rs: -------------------------------------------------------------------------------- 1 | use axum_extra::routing::{RouterExt, TypedPath}; 2 | use serde::Deserialize; 3 | 4 | #[derive(TypedPath, Deserialize)] 5 | #[typed_path("/{*rest}")] 6 | struct MyPath { 7 | rest: String, 8 | } 9 | 10 | fn main() { 11 | _ = axum::Router::<()>::new().typed_get(|_: MyPath| async {}); 12 | } 13 | -------------------------------------------------------------------------------- /axum/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 axum Contributors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /axum/src/body/mod.rs: -------------------------------------------------------------------------------- 1 | //! HTTP body utilities. 2 | 3 | #[doc(no_inline)] 4 | pub use http_body::Body as HttpBody; 5 | 6 | #[doc(no_inline)] 7 | pub use bytes::Bytes; 8 | 9 | #[doc(inline)] 10 | pub use axum_core::body::{Body, BodyDataStream}; 11 | 12 | use http_body_util::{BodyExt, Limited}; 13 | 14 | /// Converts [`Body`] into [`Bytes`] and limits the maximum size of the body. 15 | /// 16 | /// # Example 17 | /// 18 | /// ```rust 19 | /// use axum::body::{to_bytes, Body}; 20 | /// 21 | /// # async fn foo() -> Result<(), axum_core::Error> { 22 | /// let body = Body::from(vec![1, 2, 3]); 23 | /// // Use `usize::MAX` if you don't care about the maximum size. 24 | /// let bytes = to_bytes(body, usize::MAX).await?; 25 | /// assert_eq!(&bytes[..], &[1, 2, 3]); 26 | /// # Ok(()) 27 | /// # } 28 | /// ``` 29 | /// 30 | /// You can detect if the limit was hit by checking the source of the error: 31 | /// 32 | /// ```rust 33 | /// use axum::body::{to_bytes, Body}; 34 | /// use http_body_util::LengthLimitError; 35 | /// 36 | /// # #[tokio::main] 37 | /// # async fn main() { 38 | /// let body = Body::from(vec![1, 2, 3]); 39 | /// match to_bytes(body, 1).await { 40 | /// Ok(_bytes) => panic!("should have hit the limit"), 41 | /// Err(err) => { 42 | /// let source = std::error::Error::source(&err).unwrap(); 43 | /// assert!(source.is::()); 44 | /// } 45 | /// } 46 | /// # } 47 | /// ``` 48 | pub async fn to_bytes(body: Body, limit: usize) -> Result { 49 | Limited::new(body, limit) 50 | .collect() 51 | .await 52 | .map(|col| col.to_bytes()) 53 | .map_err(axum_core::Error::new) 54 | } 55 | -------------------------------------------------------------------------------- /axum/src/docs/debugging_handler_type_errors.md: -------------------------------------------------------------------------------- 1 | ## Debugging handler type errors 2 | 3 | For a function to be used as a handler it must implement the [`Handler`] trait. 4 | axum provides blanket implementations for functions that: 5 | 6 | - Are `async fn`s. 7 | - Take no more than 16 arguments that all implement `Send`. 8 | - All except the last argument implement [`FromRequestParts`]. 9 | - The last argument implements [`FromRequest`]. 10 | - Returns something that implements [`IntoResponse`]. 11 | - If a closure is used it must implement `Clone + Send` and be 12 | `'static`. 13 | - Returns a future that is `Send`. The most common way to accidentally make a 14 | future `!Send` is to hold a `!Send` type across an await. 15 | 16 | Unfortunately Rust gives poor error messages if you try to use a function 17 | that doesn't quite match what's required by [`Handler`]. 18 | 19 | You might get an error like this: 20 | 21 | ```not_rust 22 | error[E0277]: the trait bound `fn(bool) -> impl Future {handler}: Handler<_, _>` is not satisfied 23 | --> src/main.rs:13:44 24 | | 25 | 13 | let app = Router::new().route("/", get(handler)); 26 | | ^^^^^^^ the trait `Handler<_, _>` is not implemented for `fn(bool) -> impl Future {handler}` 27 | | 28 | ::: axum/src/handler/mod.rs:116:8 29 | | 30 | 116 | H: Handler, 31 | | ------------- required by this bound in `axum::routing::get` 32 | ``` 33 | 34 | This error doesn't tell you _why_ your function doesn't implement 35 | [`Handler`]. It's possible to improve the error with the [`debug_handler`] 36 | proc-macro from the [axum-macros] crate. 37 | 38 | [axum-macros]: https://docs.rs/axum-macros 39 | [`debug_handler`]: https://docs.rs/axum-macros/latest/axum_macros/attr.debug_handler.html 40 | -------------------------------------------------------------------------------- /axum/src/docs/handlers_intro.md: -------------------------------------------------------------------------------- 1 | In axum a "handler" is an async function that accepts zero or more 2 | ["extractors"](crate::extract) as arguments and returns something that 3 | can be converted [into a response](crate::response). 4 | 5 | Handlers are where your application logic lives and axum applications are built 6 | by routing between handlers. 7 | 8 | [`debug_handler`]: https://docs.rs/axum-macros/latest/axum_macros/attr.debug_handler.html 9 | -------------------------------------------------------------------------------- /axum/src/docs/method_routing/fallback.md: -------------------------------------------------------------------------------- 1 | Add a fallback service to the router. 2 | 3 | This service will be called if no routes matches the incoming request. 4 | 5 | ```rust 6 | use axum::{ 7 | Router, 8 | routing::get, 9 | handler::Handler, 10 | response::IntoResponse, 11 | http::{StatusCode, Method, Uri}, 12 | }; 13 | 14 | let handler = get(|| async {}).fallback(fallback); 15 | 16 | let app = Router::new().route("/", handler); 17 | 18 | async fn fallback(method: Method, uri: Uri) -> (StatusCode, String) { 19 | (StatusCode::NOT_FOUND, format!("`{method}` not allowed for {uri}")) 20 | } 21 | # let _: Router = app; 22 | ``` 23 | 24 | ## When used with `MethodRouter::merge` 25 | 26 | Two routers that both have a fallback cannot be merged. Doing so results in a 27 | panic: 28 | 29 | ```rust,should_panic 30 | use axum::{ 31 | routing::{get, post}, 32 | handler::Handler, 33 | response::IntoResponse, 34 | http::{StatusCode, Uri}, 35 | }; 36 | 37 | let one = get(|| async {}).fallback(fallback_one); 38 | 39 | let two = post(|| async {}).fallback(fallback_two); 40 | 41 | let method_route = one.merge(two); 42 | 43 | async fn fallback_one() -> impl IntoResponse { /* ... */ } 44 | async fn fallback_two() -> impl IntoResponse { /* ... */ } 45 | # let app: axum::Router = axum::Router::new().route("/", method_route); 46 | ``` 47 | 48 | ## Setting the `Allow` header 49 | 50 | By default `MethodRouter` will set the `Allow` header when returning `405 Method 51 | Not Allowed`. This is also done when the fallback is used unless the response 52 | generated by the fallback already sets the `Allow` header. 53 | 54 | This means if you use `fallback` to accept additional methods, you should make 55 | sure you set the `Allow` header correctly. 56 | -------------------------------------------------------------------------------- /axum/src/docs/method_routing/layer.md: -------------------------------------------------------------------------------- 1 | Apply a [`tower::Layer`] to all routes in the router. 2 | 3 | This can be used to add additional processing to a request for a group 4 | of routes. 5 | 6 | Note that the middleware is only applied to existing routes. So you have to 7 | first add your routes (and / or fallback) and then call `layer` afterwards. Additional 8 | routes added after `layer` is called will not have the middleware added. 9 | 10 | Works similarly to [`Router::layer`](super::Router::layer). See that method for 11 | more details. 12 | 13 | # Example 14 | 15 | ```rust 16 | use axum::{routing::get, Router}; 17 | use tower::limit::ConcurrencyLimitLayer; 18 | 19 | async fn handler() {} 20 | 21 | let app = Router::new().route( 22 | "/", 23 | // All requests to `GET /` will be sent through `ConcurrencyLimitLayer` 24 | get(handler).layer(ConcurrencyLimitLayer::new(64)), 25 | ); 26 | # let _: Router = app; 27 | ``` 28 | -------------------------------------------------------------------------------- /axum/src/docs/method_routing/merge.md: -------------------------------------------------------------------------------- 1 | Merge two routers into one. 2 | 3 | This is useful for breaking routers into smaller pieces and combining them 4 | into one. 5 | 6 | ```rust 7 | use axum::{ 8 | routing::{get, post}, 9 | Router, 10 | }; 11 | 12 | let get = get(|| async {}); 13 | let post = post(|| async {}); 14 | 15 | let merged = get.merge(post); 16 | 17 | let app = Router::new().route("/", merged); 18 | 19 | // Our app now accepts 20 | // - GET / 21 | // - POST / 22 | # let _: Router = app; 23 | ``` 24 | -------------------------------------------------------------------------------- /axum/src/docs/method_routing/route_layer.md: -------------------------------------------------------------------------------- 1 | Apply a [`tower::Layer`] to the router that will only run if the request matches 2 | a route. 3 | 4 | Note that the middleware is only applied to existing routes. So you have to 5 | first add your routes (and / or fallback) and then call `route_layer` 6 | afterwards. Additional routes added after `route_layer` is called will not have 7 | the middleware added. 8 | 9 | This works similarly to [`MethodRouter::layer`] except the middleware will only run if 10 | the request matches a route. This is useful for middleware that return early 11 | (such as authorization) which might otherwise convert a `405 Method Not Allowed` into a 12 | `401 Unauthorized`. 13 | 14 | # Example 15 | 16 | ```rust 17 | use axum::{ 18 | routing::get, 19 | Router, 20 | }; 21 | use tower_http::validate_request::ValidateRequestHeaderLayer; 22 | 23 | let app = Router::new().route( 24 | "/foo", 25 | get(|| async {}) 26 | .route_layer(ValidateRequestHeaderLayer::bearer("password")) 27 | ); 28 | 29 | // `GET /foo` with a valid token will receive `200 OK` 30 | // `GET /foo` with a invalid token will receive `401 Unauthorized` 31 | // `POST /FOO` with a invalid token will receive `405 Method Not Allowed` 32 | # let _: Router = app; 33 | ``` 34 | -------------------------------------------------------------------------------- /axum/src/docs/routing/method_not_allowed_fallback.md: -------------------------------------------------------------------------------- 1 | Add a fallback [`Handler`] for the case where a route exists, but the method of the request is not supported. 2 | 3 | Sets a fallback on all previously registered [`MethodRouter`]s, 4 | to be called when no matching method handler is set. 5 | 6 | ```rust,no_run 7 | use axum::{response::IntoResponse, routing::get, Router}; 8 | 9 | async fn hello_world() -> impl IntoResponse { 10 | "Hello, world!\n" 11 | } 12 | 13 | async fn default_fallback() -> impl IntoResponse { 14 | "Default fallback\n" 15 | } 16 | 17 | async fn handle_405() -> impl IntoResponse { 18 | "Method not allowed fallback" 19 | } 20 | 21 | #[tokio::main] 22 | async fn main() { 23 | let router = Router::new() 24 | .route("/", get(hello_world)) 25 | .fallback(default_fallback) 26 | .method_not_allowed_fallback(handle_405); 27 | 28 | let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); 29 | 30 | axum::serve(listener, router).await.unwrap(); 31 | } 32 | ``` 33 | 34 | The fallback only applies if there is a `MethodRouter` registered for a given path, 35 | but the method used in the request is not specified. In the example, a `GET` on 36 | `http://localhost:3000` causes the `hello_world` handler to react, while issuing a 37 | `POST` triggers `handle_405`. Calling an entirely different route, like `http://localhost:3000/hello` 38 | causes `default_fallback` to run. 39 | -------------------------------------------------------------------------------- /axum/src/docs/routing/route_layer.md: -------------------------------------------------------------------------------- 1 | Apply a [`tower::Layer`] to the router that will only run if the request matches 2 | a route. 3 | 4 | Note that the middleware is only applied to existing routes. So you have to 5 | first add your routes (and / or fallback) and then call `route_layer` 6 | afterwards. Additional routes added after `route_layer` is called will not have 7 | the middleware added. 8 | 9 | This works similarly to [`Router::layer`] except the middleware will only run if 10 | the request matches a route. This is useful for middleware that return early 11 | (such as authorization) which might otherwise convert a `404 Not Found` into a 12 | `401 Unauthorized`. 13 | 14 | This function will panic if no routes have been declared yet on the router, 15 | since the new layer will have no effect, and this is typically a bug. 16 | In generic code, you can test if that is the case first, by calling [`Router::has_routes`]. 17 | 18 | # Example 19 | 20 | ```rust 21 | use axum::{ 22 | routing::get, 23 | Router, 24 | }; 25 | use tower_http::validate_request::ValidateRequestHeaderLayer; 26 | 27 | let app = Router::new() 28 | .route("/foo", get(|| async {})) 29 | .route_layer(ValidateRequestHeaderLayer::bearer("password")); 30 | 31 | // `GET /foo` with a valid token will receive `200 OK` 32 | // `GET /foo` with a invalid token will receive `401 Unauthorized` 33 | // `GET /not-found` with a invalid token will receive `404 Not Found` 34 | # let _: Router = app; 35 | ``` 36 | -------------------------------------------------------------------------------- /axum/src/docs/routing/without_v07_checks.md: -------------------------------------------------------------------------------- 1 | Turn off checks for compatibility with route matching syntax from 0.7. 2 | 3 | This allows usage of paths starting with a colon `:` or an asterisk `*` which are otherwise prohibited. 4 | 5 | # Example 6 | 7 | ```rust 8 | use axum::{ 9 | routing::get, 10 | Router, 11 | }; 12 | 13 | let app = Router::<()>::new() 14 | .without_v07_checks() 15 | .route("/:colon", get(|| async {})) 16 | .route("/*asterisk", get(|| async {})); 17 | 18 | // Our app now accepts 19 | // - GET /:colon 20 | // - GET /*asterisk 21 | # let _: Router = app; 22 | ``` 23 | 24 | Adding such routes without calling this method first will panic. 25 | 26 | ```rust,should_panic 27 | use axum::{ 28 | routing::get, 29 | Router, 30 | }; 31 | 32 | // This panics... 33 | let app = Router::<()>::new() 34 | .route("/:colon", get(|| async {})); 35 | ``` 36 | 37 | # Merging 38 | 39 | When two routers are merged, v0.7 checks are disabled for route registrations on the resulting router if both of the two routers had them also disabled. 40 | 41 | # Nesting 42 | 43 | Each router needs to have the checks explicitly disabled. Nesting a router with the checks either enabled or disabled has no effect on the outer router. 44 | -------------------------------------------------------------------------------- /axum/src/extract/raw_query.rs: -------------------------------------------------------------------------------- 1 | use super::FromRequestParts; 2 | use http::request::Parts; 3 | use std::convert::Infallible; 4 | 5 | /// Extractor that extracts the raw query string, without parsing it. 6 | /// 7 | /// # Example 8 | /// 9 | /// ```rust,no_run 10 | /// use axum::{ 11 | /// extract::RawQuery, 12 | /// routing::get, 13 | /// Router, 14 | /// }; 15 | /// use futures_util::StreamExt; 16 | /// 17 | /// async fn handler(RawQuery(query): RawQuery) { 18 | /// // ... 19 | /// } 20 | /// 21 | /// let app = Router::new().route("/users", get(handler)); 22 | /// # let _: Router = app; 23 | /// ``` 24 | #[derive(Debug)] 25 | pub struct RawQuery(pub Option); 26 | 27 | impl FromRequestParts for RawQuery 28 | where 29 | S: Send + Sync, 30 | { 31 | type Rejection = Infallible; 32 | 33 | async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { 34 | let query = parts.uri.query().map(|query| query.to_owned()); 35 | Ok(Self(query)) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /axum/src/handler/future.rs: -------------------------------------------------------------------------------- 1 | //! Handler future types. 2 | 3 | use crate::response::Response; 4 | use axum_core::extract::Request; 5 | use futures_util::future::Map; 6 | use pin_project_lite::pin_project; 7 | use std::{convert::Infallible, future::Future, pin::Pin, task::Context}; 8 | use tower::util::Oneshot; 9 | use tower_service::Service; 10 | 11 | opaque_future! { 12 | /// The response future for [`IntoService`](super::IntoService). 13 | pub type IntoServiceFuture = 14 | Map< 15 | F, 16 | fn(Response) -> Result, 17 | >; 18 | } 19 | 20 | pin_project! { 21 | /// The response future for [`Layered`](super::Layered). 22 | pub struct LayeredFuture 23 | where 24 | S: Service, 25 | { 26 | #[pin] 27 | inner: Map, fn(Result) -> Response>, 28 | } 29 | } 30 | 31 | impl LayeredFuture 32 | where 33 | S: Service, 34 | { 35 | pub(super) fn new( 36 | inner: Map, fn(Result) -> Response>, 37 | ) -> Self { 38 | Self { inner } 39 | } 40 | } 41 | 42 | impl Future for LayeredFuture 43 | where 44 | S: Service, 45 | { 46 | type Output = Response; 47 | 48 | #[inline] 49 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> std::task::Poll { 50 | self.project().inner.poll(cx) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /axum/src/middleware/mod.rs: -------------------------------------------------------------------------------- 1 | //! Utilities for writing middleware 2 | //! 3 | #![doc = include_str!("../docs/middleware.md")] 4 | 5 | mod from_extractor; 6 | mod from_fn; 7 | mod map_request; 8 | mod map_response; 9 | 10 | pub use self::from_extractor::{ 11 | from_extractor, from_extractor_with_state, FromExtractor, FromExtractorLayer, 12 | }; 13 | pub use self::from_fn::{from_fn, from_fn_with_state, FromFn, FromFnLayer, Next}; 14 | pub use self::map_request::{ 15 | map_request, map_request_with_state, IntoMapRequestResult, MapRequest, MapRequestLayer, 16 | }; 17 | pub use self::map_response::{ 18 | map_response, map_response_with_state, MapResponse, MapResponseLayer, 19 | }; 20 | pub use crate::extension::AddExtension; 21 | 22 | pub mod future { 23 | //! Future types. 24 | 25 | pub use super::from_extractor::ResponseFuture as FromExtractorResponseFuture; 26 | pub use super::from_fn::ResponseFuture as FromFnResponseFuture; 27 | pub use super::map_request::ResponseFuture as MapRequestResponseFuture; 28 | pub use super::map_response::ResponseFuture as MapResponseResponseFuture; 29 | } 30 | -------------------------------------------------------------------------------- /axum/src/routing/future.rs: -------------------------------------------------------------------------------- 1 | //! Future types. 2 | 3 | pub use super::{ 4 | into_make_service::IntoMakeServiceFuture, 5 | route::{InfallibleRouteFuture, RouteFuture}, 6 | }; 7 | -------------------------------------------------------------------------------- /axum/src/routing/into_make_service.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | convert::Infallible, 3 | future::ready, 4 | task::{Context, Poll}, 5 | }; 6 | use tower_service::Service; 7 | 8 | /// A [`MakeService`] that produces axum router services. 9 | /// 10 | /// [`MakeService`]: tower::make::MakeService 11 | #[derive(Debug, Clone)] 12 | pub struct IntoMakeService { 13 | svc: S, 14 | } 15 | 16 | impl IntoMakeService { 17 | pub(crate) fn new(svc: S) -> Self { 18 | Self { svc } 19 | } 20 | } 21 | 22 | impl Service for IntoMakeService 23 | where 24 | S: Clone, 25 | { 26 | type Response = S; 27 | type Error = Infallible; 28 | type Future = IntoMakeServiceFuture; 29 | 30 | #[inline] 31 | fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { 32 | Poll::Ready(Ok(())) 33 | } 34 | 35 | fn call(&mut self, _target: T) -> Self::Future { 36 | IntoMakeServiceFuture::new(ready(Ok(self.svc.clone()))) 37 | } 38 | } 39 | 40 | opaque_future! { 41 | /// Response future for [`IntoMakeService`]. 42 | pub type IntoMakeServiceFuture = 43 | std::future::Ready>; 44 | } 45 | 46 | #[cfg(test)] 47 | mod tests { 48 | use super::*; 49 | 50 | #[test] 51 | fn traits() { 52 | use crate::test_helpers::*; 53 | 54 | assert_send::>(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /axum/src/routing/not_found.rs: -------------------------------------------------------------------------------- 1 | use crate::response::Response; 2 | use axum_core::response::IntoResponse; 3 | use http::{Request, StatusCode}; 4 | use std::{ 5 | convert::Infallible, 6 | future::ready, 7 | task::{Context, Poll}, 8 | }; 9 | use tower_service::Service; 10 | 11 | /// A [`Service`] that responds with `404 Not Found` to all requests. 12 | /// 13 | /// This is used as the bottom service in a method router. You shouldn't have to 14 | /// use it manually. 15 | #[derive(Clone, Copy, Debug)] 16 | pub(super) struct NotFound; 17 | 18 | impl Service> for NotFound 19 | where 20 | B: Send + 'static, 21 | { 22 | type Response = Response; 23 | type Error = Infallible; 24 | type Future = std::future::Ready>; 25 | 26 | #[inline] 27 | fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { 28 | Poll::Ready(Ok(())) 29 | } 30 | 31 | fn call(&mut self, _req: Request) -> Self::Future { 32 | ready(Ok(StatusCode::NOT_FOUND.into_response())) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /axum/src/routing/url_params.rs: -------------------------------------------------------------------------------- 1 | use crate::util::PercentDecodedStr; 2 | use http::Extensions; 3 | use matchit::Params; 4 | use std::sync::Arc; 5 | 6 | #[derive(Clone)] 7 | pub(crate) enum UrlParams { 8 | Params(Vec<(Arc, PercentDecodedStr)>), 9 | InvalidUtf8InPathParam { key: Arc }, 10 | } 11 | 12 | pub(super) fn insert_url_params(extensions: &mut Extensions, params: Params<'_, '_>) { 13 | let current_params = extensions.get_mut(); 14 | 15 | if let Some(UrlParams::InvalidUtf8InPathParam { .. }) = current_params { 16 | // nothing to do here since an error was stored earlier 17 | return; 18 | } 19 | 20 | let params = params 21 | .iter() 22 | .filter(|(key, _)| !key.starts_with(super::NEST_TAIL_PARAM)) 23 | .filter(|(key, _)| !key.starts_with(super::FALLBACK_PARAM)) 24 | .map(|(k, v)| { 25 | if let Some(decoded) = PercentDecodedStr::new(v) { 26 | Ok((Arc::from(k), decoded)) 27 | } else { 28 | Err(Arc::from(k)) 29 | } 30 | }) 31 | .collect::, _>>(); 32 | 33 | match (current_params, params) { 34 | (Some(UrlParams::InvalidUtf8InPathParam { .. }), _) => { 35 | unreachable!("we check for this state earlier in this method") 36 | } 37 | (_, Err(invalid_key)) => { 38 | extensions.insert(UrlParams::InvalidUtf8InPathParam { key: invalid_key }); 39 | } 40 | (Some(UrlParams::Params(current)), Ok(params)) => { 41 | current.extend(params); 42 | } 43 | (None, Ok(params)) => { 44 | extensions.insert(UrlParams::Params(params)); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /axum/src/test_helpers/counting_cloneable_state.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{ 2 | atomic::{AtomicBool, AtomicUsize, Ordering}, 3 | Arc, 4 | }; 5 | 6 | pub(crate) struct CountingCloneableState { 7 | state: Arc, 8 | } 9 | 10 | struct InnerState { 11 | setup_done: AtomicBool, 12 | count: AtomicUsize, 13 | } 14 | 15 | impl CountingCloneableState { 16 | pub(crate) fn new() -> Self { 17 | let inner_state = InnerState { 18 | setup_done: AtomicBool::new(false), 19 | count: AtomicUsize::new(0), 20 | }; 21 | CountingCloneableState { 22 | state: Arc::new(inner_state), 23 | } 24 | } 25 | 26 | pub(crate) fn setup_done(&self) { 27 | self.state.setup_done.store(true, Ordering::SeqCst); 28 | } 29 | 30 | pub(crate) fn count(&self) -> usize { 31 | self.state.count.load(Ordering::SeqCst) 32 | } 33 | } 34 | 35 | impl Clone for CountingCloneableState { 36 | fn clone(&self) -> Self { 37 | let state = self.state.clone(); 38 | if state.setup_done.load(Ordering::SeqCst) { 39 | let bt = std::backtrace::Backtrace::force_capture(); 40 | let bt = bt 41 | .to_string() 42 | .lines() 43 | .filter(|line| line.contains("axum") || line.contains("./src")) 44 | .collect::>() 45 | .join("\n"); 46 | println!("AppState::Clone:\n===============\n{bt}\n"); 47 | state.count.fetch_add(1, Ordering::SeqCst); 48 | } 49 | 50 | CountingCloneableState { state } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /axum/src/test_helpers/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::disallowed_names)] 2 | 3 | use crate::{extract::Request, response::Response, serve}; 4 | 5 | mod test_client; 6 | pub use self::test_client::*; 7 | 8 | #[cfg(test)] 9 | pub(crate) mod tracing_helpers; 10 | 11 | #[cfg(test)] 12 | pub(crate) mod counting_cloneable_state; 13 | 14 | #[cfg(test)] 15 | pub(crate) fn assert_send() {} 16 | #[cfg(test)] 17 | pub(crate) fn assert_sync() {} 18 | 19 | #[allow(dead_code)] 20 | pub(crate) struct NotSendSync(*const ()); 21 | -------------------------------------------------------------------------------- /axum/tests/panic_location.rs: -------------------------------------------------------------------------------- 1 | //! Separate test binary, because the panic hook is a global resource 2 | 3 | use std::{ 4 | panic::{catch_unwind, set_hook, take_hook}, 5 | path::Path, 6 | sync::OnceLock, 7 | }; 8 | 9 | use axum::{routing::get, Router}; 10 | 11 | #[test] 12 | fn routes_with_overlapping_method_routes() { 13 | static PANIC_LOCATION_FILE: OnceLock = OnceLock::new(); 14 | 15 | let default_hook = take_hook(); 16 | set_hook(Box::new(|panic_info| { 17 | if let Some(location) = panic_info.location() { 18 | _ = PANIC_LOCATION_FILE.set(location.file().to_owned()); 19 | } 20 | })); 21 | 22 | let result = catch_unwind(|| { 23 | async fn handler() {} 24 | 25 | let _: Router = Router::new() 26 | .route("/foo/bar", get(handler)) 27 | .route("/foo/bar", get(handler)); 28 | }); 29 | set_hook(default_hook); 30 | 31 | let panic_payload = result.unwrap_err(); 32 | let panic_msg = panic_payload.downcast_ref::().unwrap(); 33 | 34 | assert_eq!( 35 | panic_msg, 36 | "Overlapping method route. Handler for `GET /foo/bar` already exists" 37 | ); 38 | 39 | let file = PANIC_LOCATION_FILE.get().unwrap(); 40 | assert_eq!(Path::new(file).file_name().unwrap(), "panic_location.rs"); 41 | } 42 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | [advisories] 2 | vulnerability = "deny" 3 | unmaintained = "warn" 4 | notice = "warn" 5 | ignore = [] 6 | 7 | [licenses] 8 | unlicensed = "warn" 9 | allow = [] 10 | deny = [] 11 | copyleft = "warn" 12 | allow-osi-fsf-free = "either" 13 | confidence-threshold = 0.8 14 | 15 | [bans] 16 | multiple-versions = "deny" 17 | highlight = "all" 18 | skip-tree = [ 19 | # currently duplicated through header, reqwest, tower-http and cookie 20 | # C.f. https://github.com/tokio-rs/axum/pull/1641 21 | { name = "base64" }, 22 | # parking_lot pulls in old versions of windows-sys 23 | { name = "windows-sys" }, 24 | # old version pulled in by rustls via ring 25 | { name = "spin" }, 26 | # lots still pulls in syn 1.x 27 | { name = "syn" }, 28 | # until 1.0 is out we're pulling in both 0.14 and 1.0-rc.x 29 | { name = "hyper" }, 30 | # pulled in by tracing-subscriber 31 | { name = "regex-syntax" }, 32 | # pulled in by tracing-subscriber 33 | { name = "regex-automata" }, 34 | # pulled in by hyper 35 | { name = "socket2" }, 36 | # hyper-util hasn't upgraded to 0.5 yet, but it's the same service / layer 37 | # crates beneath 38 | { name = "tower" }, 39 | # tower hasn't upgraded to 1.0 yet 40 | { name = "sync_wrapper" }, 41 | ] 42 | 43 | [sources] 44 | unknown-registry = "warn" 45 | unknown-git = "warn" 46 | allow-git = [] 47 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | This folder contains numerous examples showing how to use axum. Each example is 4 | setup as its own crate so its dependencies are clear. 5 | 6 | For a list of what the community built with axum, please see the list 7 | [here](../ECOSYSTEM.md). 8 | -------------------------------------------------------------------------------- /examples/anyhow-error-response/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-anyhow-error-response" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | anyhow = "1.0" 9 | axum = { path = "../../axum" } 10 | tokio = { version = "1.0", features = ["full"] } 11 | 12 | [dev-dependencies] 13 | http-body-util = "0.1.0" 14 | tower = { version = "0.5.2", features = ["util"] } 15 | -------------------------------------------------------------------------------- /examples/async-graphql/README.md: -------------------------------------------------------------------------------- 1 | See . 2 | -------------------------------------------------------------------------------- /examples/auto-reload/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "auto-reload" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | listenfd = "1.0.1" 10 | tokio = { version = "1.0", features = ["full"] } 11 | -------------------------------------------------------------------------------- /examples/auto-reload/README.md: -------------------------------------------------------------------------------- 1 | # auto-reload 2 | 3 | This example shows how you can set up a development environment for your axum 4 | service such that whenever the source code changes, the app is recompiled and 5 | restarted. It uses `listenfd` to be able to migrate connections from an old 6 | version of the app to a newly-compiled version. 7 | 8 | ## Setup 9 | 10 | ```sh 11 | cargo install cargo-watch systemfd 12 | ``` 13 | 14 | ## Running 15 | 16 | ```sh 17 | systemfd --no-pid -s http::3000 -- cargo watch -x run 18 | ``` 19 | -------------------------------------------------------------------------------- /examples/auto-reload/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Run with 2 | //! 3 | //! ```not_rust 4 | //! cargo run -p auto-reload 5 | //! ``` 6 | 7 | use axum::{response::Html, routing::get, Router}; 8 | use listenfd::ListenFd; 9 | use tokio::net::TcpListener; 10 | 11 | #[tokio::main] 12 | async fn main() { 13 | // build our application with a route 14 | let app = Router::new().route("/", get(handler)); 15 | 16 | let mut listenfd = ListenFd::from_env(); 17 | let listener = match listenfd.take_tcp_listener(0).unwrap() { 18 | // if we are given a tcp listener on listen fd 0, we use that one 19 | Some(listener) => { 20 | listener.set_nonblocking(true).unwrap(); 21 | TcpListener::from_std(listener).unwrap() 22 | } 23 | // otherwise fall back to local listening 24 | None => TcpListener::bind("127.0.0.1:3000").await.unwrap(), 25 | }; 26 | 27 | // run it 28 | println!("listening on {}", listener.local_addr().unwrap()); 29 | axum::serve(listener, app).await.unwrap(); 30 | } 31 | 32 | async fn handler() -> Html<&'static str> { 33 | Html("

Hello, World!

") 34 | } 35 | -------------------------------------------------------------------------------- /examples/chat/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-chat" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["ws"] } 9 | futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } 10 | tokio = { version = "1", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /examples/compression/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-compression" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | serde_json = "1" 10 | tokio = { version = "1", features = ["macros", "rt-multi-thread"] } 11 | tower = "0.5.2" 12 | tower-http = { version = "0.6.1", features = ["compression-full", "decompression-full"] } 13 | tracing = "0.1" 14 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 15 | 16 | [dev-dependencies] 17 | assert-json-diff = "2.0" 18 | brotli = "6.0" 19 | flate2 = "1" 20 | http = "1" 21 | zstd = "0.13" 22 | -------------------------------------------------------------------------------- /examples/compression/README.md: -------------------------------------------------------------------------------- 1 | # compression 2 | 3 | This example shows how to: 4 | - automatically decompress request bodies when necessary 5 | - compress response bodies based on the `accept` header. 6 | 7 | ## Running 8 | 9 | ``` 10 | cargo run -p example-compression 11 | ``` 12 | 13 | ## Sending compressed requests 14 | 15 | ``` 16 | curl -v -g 'http://localhost:3000/' \ 17 | -H "Content-Type: application/json" \ 18 | -H "Content-Encoding: gzip" \ 19 | --compressed \ 20 | --data-binary @data/products.json.gz 21 | ``` 22 | 23 | (Notice the `Content-Encoding: gzip` in the request, and `content-encoding: gzip` in the response.) 24 | 25 | ## Sending uncompressed requests 26 | 27 | ``` 28 | curl -v -g 'http://localhost:3000/' \ 29 | -H "Content-Type: application/json" \ 30 | --compressed \ 31 | --data-binary @data/products.json 32 | ``` 33 | -------------------------------------------------------------------------------- /examples/compression/data/products.json: -------------------------------------------------------------------------------- 1 | { 2 | "products": [ 3 | { 4 | "id": 1, 5 | "name": "Product 1" 6 | }, 7 | { 8 | "id": 2, 9 | "name": "Product 2" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /examples/compression/data/products.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tokio-rs/axum/7eabf7e645caf4e8e974cbdd886ab99eb2766d1d/examples/compression/data/products.json.gz -------------------------------------------------------------------------------- /examples/compression/src/main.rs: -------------------------------------------------------------------------------- 1 | use axum::{routing::post, Json, Router}; 2 | use serde_json::Value; 3 | use tower::ServiceBuilder; 4 | use tower_http::{compression::CompressionLayer, decompression::RequestDecompressionLayer}; 5 | use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; 6 | 7 | #[cfg(test)] 8 | mod tests; 9 | 10 | #[tokio::main] 11 | async fn main() { 12 | tracing_subscriber::registry() 13 | .with( 14 | tracing_subscriber::EnvFilter::try_from_default_env() 15 | .unwrap_or_else(|_| format!("{}=trace", env!("CARGO_CRATE_NAME")).into()), 16 | ) 17 | .with(tracing_subscriber::fmt::layer()) 18 | .init(); 19 | 20 | let app: Router = app(); 21 | 22 | let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") 23 | .await 24 | .unwrap(); 25 | tracing::debug!("listening on {}", listener.local_addr().unwrap()); 26 | axum::serve(listener, app).await.unwrap(); 27 | } 28 | 29 | fn app() -> Router { 30 | Router::new().route("/", post(root)).layer( 31 | ServiceBuilder::new() 32 | .layer(RequestDecompressionLayer::new()) 33 | .layer(CompressionLayer::new()), 34 | ) 35 | } 36 | 37 | async fn root(Json(value): Json) -> Json { 38 | Json(value) 39 | } 40 | -------------------------------------------------------------------------------- /examples/consume-body-in-extractor-or-middleware/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-consume-body-in-extractor-or-middleware" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | http-body-util = "0.1.0" 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /examples/cors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-cors" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tower-http = { version = "0.6.1", features = ["cors"] } 11 | -------------------------------------------------------------------------------- /examples/customize-extractor-error/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-customize-extractor-error" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["macros"] } 9 | axum-extra = { path = "../../axum-extra" } 10 | serde = { version = "1.0", features = ["derive"] } 11 | serde_json = "1.0" 12 | thiserror = "1.0" 13 | tokio = { version = "1.20", features = ["full"] } 14 | tracing = "0.1" 15 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 16 | -------------------------------------------------------------------------------- /examples/customize-extractor-error/README.md: -------------------------------------------------------------------------------- 1 | This example explores 3 different ways you can create custom rejections for 2 | already existing extractors 3 | 4 | - [`with_rejection`](src/with_rejection.rs): Uses 5 | `axum_extra::extract::WithRejection` to transform one rejection into another 6 | - [`derive_from_request`](src/derive_from_request.rs): Uses the 7 | `axum::extract::FromRequest` derive macro to wrap another extractor and 8 | customize the rejection 9 | - [`custom_extractor`](src/custom_extractor.rs): Manual implementation of 10 | `FromRequest` that wraps another extractor 11 | 12 | Run with 13 | 14 | ```sh 15 | cargo run -p example-customize-extractor-error 16 | ``` 17 | -------------------------------------------------------------------------------- /examples/customize-extractor-error/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Run with 2 | //! 3 | //! ```not_rust 4 | //! cargo run -p example-customize-extractor-error 5 | //! ``` 6 | 7 | mod custom_extractor; 8 | mod derive_from_request; 9 | mod with_rejection; 10 | 11 | use axum::{routing::post, Router}; 12 | use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; 13 | 14 | #[tokio::main] 15 | async fn main() { 16 | tracing_subscriber::registry() 17 | .with( 18 | tracing_subscriber::EnvFilter::try_from_default_env() 19 | .unwrap_or_else(|_| format!("{}=trace", env!("CARGO_CRATE_NAME")).into()), 20 | ) 21 | .with(tracing_subscriber::fmt::layer()) 22 | .init(); 23 | 24 | // Build our application with some routes 25 | let app = Router::new() 26 | .route("/with-rejection", post(with_rejection::handler)) 27 | .route("/custom-extractor", post(custom_extractor::handler)) 28 | .route("/derive-from-request", post(derive_from_request::handler)); 29 | 30 | // Run our application 31 | let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") 32 | .await 33 | .unwrap(); 34 | tracing::debug!("listening on {}", listener.local_addr().unwrap()); 35 | axum::serve(listener, app).await.unwrap(); 36 | } 37 | -------------------------------------------------------------------------------- /examples/customize-path-rejection/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-customize-path-rejection" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | serde = { version = "1.0", features = ["derive"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /examples/dependency-injection/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-dependency-injection" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["tracing", "macros"] } 9 | serde = { version = "1.0", features = ["derive"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | uuid = { version = "1.0", features = ["serde", "v4"] } 14 | -------------------------------------------------------------------------------- /examples/diesel-async-postgres/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-diesel-async-postgres" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["macros"] } 9 | bb8 = "0.8" 10 | diesel = "2" 11 | diesel-async = { version = "0.5", features = ["postgres", "bb8"] } 12 | serde = { version = "1.0", features = ["derive"] } 13 | tokio = { version = "1.0", features = ["full"] } 14 | tracing = "0.1" 15 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 16 | -------------------------------------------------------------------------------- /examples/diesel-async-postgres/migrations/2023-03-14-180127_add_users/down.sql: -------------------------------------------------------------------------------- 1 | -- This file should undo anything in "up.sql" 2 | DROP TABLE "users"; 3 | -------------------------------------------------------------------------------- /examples/diesel-async-postgres/migrations/2023-03-14-180127_add_users/up.sql: -------------------------------------------------------------------------------- 1 | -- Your SQL goes here 2 | CREATE TABLE "users"( 3 | "id" SERIAL PRIMARY KEY, 4 | "name" TEXT NOT NULL, 5 | "hair_color" TEXT 6 | ); 7 | -------------------------------------------------------------------------------- /examples/diesel-postgres/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-diesel-postgres" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["macros"] } 9 | deadpool-diesel = { version = "0.6.1", features = ["postgres"] } 10 | diesel = { version = "2", features = ["postgres"] } 11 | diesel_migrations = "2" 12 | serde = { version = "1.0", features = ["derive"] } 13 | tokio = { version = "1.0", features = ["full"] } 14 | tracing = "0.1" 15 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 16 | -------------------------------------------------------------------------------- /examples/diesel-postgres/migrations/2023-03-14-180127_add_users/down.sql: -------------------------------------------------------------------------------- 1 | -- This file should undo anything in "up.sql" 2 | DROP TABLE "users"; 3 | -------------------------------------------------------------------------------- /examples/diesel-postgres/migrations/2023-03-14-180127_add_users/up.sql: -------------------------------------------------------------------------------- 1 | -- Your SQL goes here 2 | CREATE TABLE "users"( 3 | "id" SERIAL PRIMARY KEY, 4 | "name" TEXT NOT NULL, 5 | "hair_color" TEXT 6 | ); 7 | -------------------------------------------------------------------------------- /examples/error-handling/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-error-handling" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["macros"] } 9 | serde = { version = "1.0", features = ["derive"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tower-http = { version = "0.6.1", features = ["trace"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/form/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-form" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | serde = { version = "1.0", features = ["derive"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | 14 | [dev-dependencies] 15 | http-body-util = "0.1.3" 16 | mime = "0.3.17" 17 | tower = "0.5.2" 18 | -------------------------------------------------------------------------------- /examples/global-404-handler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-global-404-handler" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tracing = "0.1" 11 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 12 | -------------------------------------------------------------------------------- /examples/global-404-handler/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Run with 2 | //! 3 | //! ```not_rust 4 | //! cargo run -p example-global-404-handler 5 | //! ``` 6 | 7 | use axum::{ 8 | http::StatusCode, 9 | response::{Html, IntoResponse}, 10 | routing::get, 11 | Router, 12 | }; 13 | use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; 14 | 15 | #[tokio::main] 16 | async fn main() { 17 | tracing_subscriber::registry() 18 | .with( 19 | tracing_subscriber::EnvFilter::try_from_default_env() 20 | .unwrap_or_else(|_| format!("{}=debug", env!("CARGO_CRATE_NAME")).into()), 21 | ) 22 | .with(tracing_subscriber::fmt::layer()) 23 | .init(); 24 | 25 | // build our application with a route 26 | let app = Router::new().route("/", get(handler)); 27 | 28 | // add a fallback service for handling routes to unknown paths 29 | let app = app.fallback(handler_404); 30 | 31 | // run it 32 | let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") 33 | .await 34 | .unwrap(); 35 | tracing::debug!("listening on {}", listener.local_addr().unwrap()); 36 | axum::serve(listener, app).await.unwrap(); 37 | } 38 | 39 | async fn handler() -> Html<&'static str> { 40 | Html("

Hello, World!

") 41 | } 42 | 43 | async fn handler_404() -> impl IntoResponse { 44 | (StatusCode::NOT_FOUND, "nothing to see here") 45 | } 46 | -------------------------------------------------------------------------------- /examples/graceful-shutdown/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-graceful-shutdown" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["tracing"] } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tower-http = { version = "0.6.1", features = ["timeout", "trace"] } 11 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 12 | -------------------------------------------------------------------------------- /examples/handle-head-request/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-handle-head-request" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | 11 | [dev-dependencies] 12 | http-body-util = "0.1.0" 13 | hyper = { version = "1.0.0", features = ["full"] } 14 | tower = { version = "0.5.2", features = ["util"] } 15 | -------------------------------------------------------------------------------- /examples/hello-world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-hello-world" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | -------------------------------------------------------------------------------- /examples/hello-world/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Run with 2 | //! 3 | //! ```not_rust 4 | //! cargo run -p example-hello-world 5 | //! ``` 6 | 7 | use axum::{response::Html, routing::get, Router}; 8 | 9 | #[tokio::main] 10 | async fn main() { 11 | // build our application with a route 12 | let app = Router::new().route("/", get(handler)); 13 | 14 | // run it 15 | let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") 16 | .await 17 | .unwrap(); 18 | println!("listening on {}", listener.local_addr().unwrap()); 19 | axum::serve(listener, app).await.unwrap(); 20 | } 21 | 22 | async fn handler() -> Html<&'static str> { 23 | Html("

Hello, World!

") 24 | } 25 | -------------------------------------------------------------------------------- /examples/http-proxy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-http-proxy" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | hyper = { version = "1", features = ["full"] } 10 | hyper-util = "0.1.1" 11 | tokio = { version = "1.0", features = ["full"] } 12 | tower = { version = "0.5.2", features = ["make", "util"] } 13 | tracing = "0.1" 14 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 15 | -------------------------------------------------------------------------------- /examples/jwt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-jwt" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | axum-extra = { path = "../../axum-extra", features = ["typed-header"] } 10 | jsonwebtoken = "9.3" 11 | serde = { version = "1.0", features = ["derive"] } 12 | serde_json = "1.0" 13 | tokio = { version = "1.0", features = ["full"] } 14 | tracing = "0.1" 15 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 16 | -------------------------------------------------------------------------------- /examples/key-value-store/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-key-value-store" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tower = { version = "0.5.2", features = ["util", "timeout", "load-shed", "limit"] } 11 | tower-http = { version = "0.6.1", features = [ 12 | "add-extension", 13 | "auth", 14 | "compression-full", 15 | "limit", 16 | "trace", 17 | ] } 18 | tracing = "0.1" 19 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 20 | -------------------------------------------------------------------------------- /examples/low-level-native-tls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-low-level-native-tls" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | futures-util = { version = "0.3", default-features = false } 10 | hyper = { version = "1.0.0", features = ["full"] } 11 | hyper-util = { version = "0.1" } 12 | tokio = { version = "1", features = ["full"] } 13 | tokio-native-tls = "0.3.1" 14 | tower-service = "0.3.2" 15 | tracing = "0.1" 16 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 17 | -------------------------------------------------------------------------------- /examples/low-level-native-tls/self_signed_certs/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL 3 | BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X 5 | DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR 6 | BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 7 | IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 8 | MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0 9 | daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4 10 | kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq 11 | dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT 12 | bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6 13 | J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK 14 | NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0 15 | yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W 16 | ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU 17 | XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg 18 | +MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9 19 | Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24 20 | fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr 21 | AopvZ09uEQ== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /examples/low-level-native-tls/self_signed_certs/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD 3 | BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS 4 | tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw 5 | RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l 6 | YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t 7 | HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV 8 | W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB 9 | 12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI 10 | +2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw 11 | zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt 12 | fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty 13 | RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT 14 | ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1 15 | myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ 16 | XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY 17 | 5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD 18 | ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD 19 | ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27 20 | k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7 21 | wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV 22 | 5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg 23 | 3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa 24 | Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C 25 | rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m 26 | y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW 27 | w37pCS7ykL+7gp7V0WShYsw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/low-level-openssl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-low-level-openssl" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | futures-util = { version = "0.3", default-features = false, features = ["alloc"] } 10 | hyper = { version = "1.0.0", features = ["full"] } 11 | hyper-util = { version = "0.1" } 12 | openssl = "0.10" 13 | tokio = { version = "1", features = ["full"] } 14 | tokio-openssl = "0.6" 15 | tower = { version = "0.5.2", features = ["make"] } 16 | tracing = "0.1" 17 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 18 | -------------------------------------------------------------------------------- /examples/low-level-openssl/self_signed_certs/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL 3 | BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X 5 | DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR 6 | BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 7 | IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 8 | MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0 9 | daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4 10 | kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq 11 | dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT 12 | bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6 13 | J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK 14 | NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0 15 | yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W 16 | ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU 17 | XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg 18 | +MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9 19 | Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24 20 | fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr 21 | AopvZ09uEQ== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /examples/low-level-openssl/self_signed_certs/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD 3 | BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS 4 | tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw 5 | RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l 6 | YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t 7 | HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV 8 | W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB 9 | 12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI 10 | +2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw 11 | zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt 12 | fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty 13 | RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT 14 | ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1 15 | myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ 16 | XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY 17 | 5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD 18 | ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD 19 | ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27 20 | k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7 21 | wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV 22 | 5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg 23 | 3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa 24 | Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C 25 | rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m 26 | y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW 27 | w37pCS7ykL+7gp7V0WShYsw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/low-level-rustls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-low-level-rustls" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | futures-util = { version = "0.3", default-features = false } 10 | hyper = { version = "1.0.0", features = ["full"] } 11 | hyper-util = { version = "0.1", features = ["http2"] } 12 | tokio = { version = "1", features = ["full"] } 13 | tokio-rustls = "0.26" 14 | tower-service = "0.3.2" 15 | tracing = "0.1" 16 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 17 | -------------------------------------------------------------------------------- /examples/low-level-rustls/self_signed_certs/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL 3 | BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X 5 | DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR 6 | BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 7 | IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 8 | MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0 9 | daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4 10 | kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq 11 | dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT 12 | bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6 13 | J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK 14 | NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0 15 | yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W 16 | ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU 17 | XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg 18 | +MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9 19 | Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24 20 | fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr 21 | AopvZ09uEQ== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /examples/low-level-rustls/self_signed_certs/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD 3 | BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS 4 | tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw 5 | RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l 6 | YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t 7 | HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV 8 | W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB 9 | 12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI 10 | +2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw 11 | zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt 12 | fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty 13 | RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT 14 | ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1 15 | myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ 16 | XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY 17 | 5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD 18 | ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD 19 | ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27 20 | k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7 21 | wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV 22 | 5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg 23 | 3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa 24 | Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C 25 | rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m 26 | y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW 27 | w37pCS7ykL+7gp7V0WShYsw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/mongodb/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-mongodb" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | mongodb = "3.1.0" 10 | serde = { version = "1.0", features = ["derive"] } 11 | tokio = { version = "1.0", features = ["full"] } 12 | tower-http = { version = "0.6.1", features = ["add-extension", "trace"] } 13 | tracing = "0.1" 14 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 15 | -------------------------------------------------------------------------------- /examples/multipart-form/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-multipart-form" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["multipart"] } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tower-http = { version = "0.6.1", features = ["limit", "trace"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /examples/oauth/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-oauth" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | anyhow = "1" 9 | async-session = "3.0.0" 10 | axum = { path = "../../axum" } 11 | axum-extra = { path = "../../axum-extra", features = ["typed-header"] } 12 | http = "1.0.0" 13 | oauth2 = "4.1" 14 | # Use Rustls because it makes it easier to cross-compile on CI 15 | reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"] } 16 | serde = { version = "1.0", features = ["derive"] } 17 | tokio = { version = "1.0", features = ["full"] } 18 | tracing = "0.1" 19 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 20 | -------------------------------------------------------------------------------- /examples/parse-body-based-on-content-type/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-parse-body-based-on-content-type" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | serde = { version = "1.0", features = ["derive"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /examples/print-request-response/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-print-request-response" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | http-body-util = "0.1.0" 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /examples/prometheus-metrics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-prometheus-metrics" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | metrics = { version = "0.23", default-features = false } 10 | metrics-exporter-prometheus = { version = "0.15", default-features = false } 11 | tokio = { version = "1.0", features = ["full"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/query-params-with-empty-strings/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-query-params-with-empty-strings" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | http-body-util = "0.1.0" 10 | serde = { version = "1.0", features = ["derive"] } 11 | tokio = { version = "1.0", features = ["full"] } 12 | tower = { version = "0.5.2", features = ["util"] } 13 | -------------------------------------------------------------------------------- /examples/readme/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-readme" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | serde = { version = "1.0", features = ["derive"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /examples/request-id/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-request-id" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tower = "0.5.2" 11 | tower-http = { version = "0.5", features = ["request-id", "trace"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/reqwest-response/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-reqwest-response" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | reqwest = { version = "0.12", features = ["stream"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tokio-stream = "0.1" 12 | tower-http = { version = "0.6.1", features = ["trace"] } 13 | tracing = "0.1" 14 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 15 | -------------------------------------------------------------------------------- /examples/reverse-proxy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-reverse-proxy" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | axum = { path = "../../axum" } 8 | hyper = { version = "1.0.0", features = ["full"] } 9 | hyper-util = { version = "0.1.1", features = ["client-legacy"] } 10 | tokio = { version = "1", features = ["full"] } 11 | -------------------------------------------------------------------------------- /examples/routes-and-handlers-close-together/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-routes-and-handlers-close-together" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | -------------------------------------------------------------------------------- /examples/routes-and-handlers-close-together/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Run with 2 | //! 3 | //! ```not_rust 4 | //! cargo run -p example-routes-and-handlers-close-together 5 | //! ``` 6 | 7 | use axum::{ 8 | routing::{get, post, MethodRouter}, 9 | Router, 10 | }; 11 | 12 | #[tokio::main] 13 | async fn main() { 14 | let app = Router::new() 15 | .merge(root()) 16 | .merge(get_foo()) 17 | .merge(post_foo()); 18 | 19 | let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") 20 | .await 21 | .unwrap(); 22 | println!("listening on {}", listener.local_addr().unwrap()); 23 | axum::serve(listener, app).await.unwrap(); 24 | } 25 | 26 | fn root() -> Router { 27 | async fn handler() -> &'static str { 28 | "Hello, World!" 29 | } 30 | 31 | route("/", get(handler)) 32 | } 33 | 34 | fn get_foo() -> Router { 35 | async fn handler() -> &'static str { 36 | "Hi from `GET /foo`" 37 | } 38 | 39 | route("/foo", get(handler)) 40 | } 41 | 42 | fn post_foo() -> Router { 43 | async fn handler() -> &'static str { 44 | "Hi from `POST /foo`" 45 | } 46 | 47 | route("/foo", post(handler)) 48 | } 49 | 50 | fn route(path: &str, method_router: MethodRouter<()>) -> Router { 51 | Router::new().route(path, method_router) 52 | } 53 | -------------------------------------------------------------------------------- /examples/serve-with-hyper/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-serve-with-hyper" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | hyper = { version = "1.0", features = [] } 10 | hyper-util = { version = "0.1", features = ["tokio", "server-auto", "http1"] } 11 | tokio = { version = "1.0", features = ["full"] } 12 | tower = { version = "0.5.2", features = ["util"] } 13 | -------------------------------------------------------------------------------- /examples/simple-router-wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-simple-router-wasm" 3 | version = "0.1.0" 4 | edition = "2018" 5 | publish = false 6 | 7 | [package.metadata.cargo-machete] 8 | ignored = ["axum-extra"] 9 | 10 | [dependencies] 11 | # `default-features = false` to not depend on tokio features which don't support wasm 12 | # you can still pull in tokio manually and only add features that tokio supports for wasm 13 | axum = { path = "../../axum", default-features = false } 14 | # we don't strictly use axum-extra in this example but wanna make sure that 15 | # works in wasm as well 16 | axum-extra = { path = "../../axum-extra", default-features = false } 17 | futures-executor = "0.3.21" 18 | http = "1.0.0" 19 | tower-service = "0.3.1" 20 | -------------------------------------------------------------------------------- /examples/sqlx-postgres/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-sqlx-postgres" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tracing = "0.1" 11 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 12 | 13 | sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "any", "postgres"] } 14 | -------------------------------------------------------------------------------- /examples/sse/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-sse" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | axum-extra = { path = "../../axum-extra", features = ["typed-header"] } 10 | futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } 11 | headers = "0.4" 12 | tokio = { version = "1.0", features = ["full"] } 13 | tokio-stream = "0.1" 14 | tower-http = { version = "0.6.1", features = ["fs", "trace"] } 15 | tracing = "0.1" 16 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 17 | 18 | [dev-dependencies] 19 | eventsource-stream = "0.2" 20 | reqwest = { version = "0.12", features = ["stream"] } 21 | reqwest-eventsource = "0.6" 22 | -------------------------------------------------------------------------------- /examples/sse/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/sse/assets/script.js: -------------------------------------------------------------------------------- 1 | var eventSource = new EventSource('sse'); 2 | 3 | eventSource.onmessage = function(event) { 4 | console.log('Message from server ', event.data); 5 | } 6 | -------------------------------------------------------------------------------- /examples/static-file-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-static-file-server" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tower = { version = "0.5.2", features = ["util"] } 11 | tower-http = { version = "0.6.1", features = ["fs", "trace"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/static-file-server/assets/index.html: -------------------------------------------------------------------------------- 1 | Hi from index.html 2 | -------------------------------------------------------------------------------- /examples/static-file-server/assets/script.js: -------------------------------------------------------------------------------- 1 | console.log("Hello, World!"); 2 | -------------------------------------------------------------------------------- /examples/stream-to-file/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-stream-to-file" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["multipart"] } 9 | futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tokio-util = { version = "0.7", features = ["io"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/templates-minijinja/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-templates-minijinja" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | minijinja = "2.3.1" 10 | tokio = { version = "1.0", features = ["full"] } 11 | -------------------------------------------------------------------------------- /examples/templates-minijinja/templates/about.jinja: -------------------------------------------------------------------------------- 1 | {% extends "layout" %} 2 | {% block title %}{{ super() }} | {{ title }} {% endblock %} 3 | {% block body %} 4 |

{{ title }}

5 |

{{ about_text }}

6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /examples/templates-minijinja/templates/content.jinja: -------------------------------------------------------------------------------- 1 | {% extends "layout" %} 2 | {% block title %}{{ super() }} | {{ title }} {% endblock %} 3 | {% block body %} 4 |

{{ title }}

5 | {% for data_entry in entries %} 6 |
    7 |
  • {{ data_entry }}
  • 8 |
9 | {% endfor %} 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /examples/templates-minijinja/templates/home.jinja: -------------------------------------------------------------------------------- 1 | {% extends "layout" %} 2 | {% block title %}{{ super() }} | {{ title }} {% endblock %} 3 | {% block body %} 4 |

{{ title }}

5 |

{{ welcome_text }}

6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /examples/templates-minijinja/templates/layout.jinja: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% block title %}Website Name{% endblock %} 4 | 5 |
12 | {% block body %}{% endblock %} 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/templates/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-templates" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | askama = "0.12" 9 | axum = { path = "../../axum" } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | 14 | [dev-dependencies] 15 | http-body-util = "0.1.0" 16 | tower = { version = "0.5.2", features = ["util"] } 17 | -------------------------------------------------------------------------------- /examples/templates/templates/hello.html: -------------------------------------------------------------------------------- 1 |

Hello, {{ name }}!

2 | -------------------------------------------------------------------------------- /examples/testing-websockets/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-testing-websockets" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["ws"] } 9 | futures-channel = "0.3" 10 | futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } 11 | tokio = { version = "1.0", features = ["full"] } 12 | tokio-tungstenite = "0.26" 13 | -------------------------------------------------------------------------------- /examples/testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-testing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | http-body-util = "0.1.0" 10 | hyper-util = { version = "0.1", features = ["client", "http1", "client-legacy"] } 11 | mime = "0.3" 12 | serde_json = "1.0" 13 | tokio = { version = "1.0", features = ["full"] } 14 | tower-http = { version = "0.6.1", features = ["trace"] } 15 | tracing = "0.1" 16 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 17 | 18 | [dev-dependencies] 19 | tower = { version = "0.5.2", features = ["util"] } 20 | -------------------------------------------------------------------------------- /examples/tls-graceful-shutdown/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-tls-graceful-shutdown" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | axum-extra = { path = "../../axum-extra" } 10 | axum-server = { version = "0.7", features = ["tls-rustls"] } 11 | tokio = { version = "1", features = ["full"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/tls-graceful-shutdown/self_signed_certs/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL 3 | BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X 5 | DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR 6 | BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 7 | IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 8 | MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0 9 | daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4 10 | kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq 11 | dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT 12 | bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6 13 | J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK 14 | NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0 15 | yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W 16 | ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU 17 | XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg 18 | +MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9 19 | Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24 20 | fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr 21 | AopvZ09uEQ== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /examples/tls-graceful-shutdown/self_signed_certs/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD 3 | BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS 4 | tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw 5 | RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l 6 | YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t 7 | HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV 8 | W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB 9 | 12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI 10 | +2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw 11 | zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt 12 | fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty 13 | RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT 14 | ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1 15 | myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ 16 | XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY 17 | 5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD 18 | ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD 19 | ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27 20 | k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7 21 | wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV 22 | 5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg 23 | 3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa 24 | Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C 25 | rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m 26 | y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW 27 | w37pCS7ykL+7gp7V0WShYsw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/tls-rustls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-tls-rustls" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | axum-extra = { path = "../../axum-extra" } 10 | axum-server = { version = "0.7", features = ["tls-rustls"] } 11 | tokio = { version = "1", features = ["full"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/tls-rustls/self_signed_certs/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL 3 | BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X 5 | DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR 6 | BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 7 | IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 8 | MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0 9 | daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4 10 | kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq 11 | dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT 12 | bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6 13 | J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK 14 | NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0 15 | yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W 16 | ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU 17 | XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg 18 | +MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9 19 | Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24 20 | fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr 21 | AopvZ09uEQ== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /examples/tls-rustls/self_signed_certs/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD 3 | BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS 4 | tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw 5 | RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l 6 | YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t 7 | HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV 8 | W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB 9 | 12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI 10 | +2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw 11 | zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt 12 | fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty 13 | RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT 14 | ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1 15 | myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ 16 | XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY 17 | 5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD 18 | ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD 19 | ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27 20 | k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7 21 | wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV 22 | 5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg 23 | 3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa 24 | Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C 25 | rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m 26 | y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW 27 | w37pCS7ykL+7gp7V0WShYsw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/todos/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-todos" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | serde = { version = "1.0", features = ["derive"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tower = { version = "0.5.2", features = ["util", "timeout"] } 12 | tower-http = { version = "0.6.1", features = ["add-extension", "trace"] } 13 | tracing = "0.1" 14 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 15 | uuid = { version = "1.0", features = ["serde", "v4"] } 16 | -------------------------------------------------------------------------------- /examples/tokio-postgres/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-tokio-postgres" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | bb8 = "0.9.0" 10 | bb8-postgres = "0.9.0" 11 | tokio = { version = "1.0", features = ["full"] } 12 | tokio-postgres = "0.7.2" 13 | tracing = "0.1" 14 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 15 | -------------------------------------------------------------------------------- /examples/tokio-redis/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-tokio-redis" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | bb8 = "0.8.5" 10 | bb8-redis = "0.17.0" 11 | redis = "0.27.2" 12 | tokio = { version = "1.0", features = ["full"] } 13 | tracing = "0.1" 14 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 15 | -------------------------------------------------------------------------------- /examples/tracing-aka-logging/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-tracing-aka-logging" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["tracing"] } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tower-http = { version = "0.6.1", features = ["trace"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /examples/unix-domain-socket/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-unix-domain-socket" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | http-body-util = "0.1" 10 | hyper = { version = "1.0.0", features = ["full"] } 11 | hyper-util = { version = "0.1", features = ["tokio", "server-auto", "http1"] } 12 | tokio = { version = "1.0", features = ["full"] } 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/validator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "example-validator" 4 | publish = false 5 | version = "0.1.0" 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | serde = { version = "1.0", features = ["derive"] } 10 | thiserror = "1.0.29" 11 | tokio = { version = "1.0", features = ["full"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | validator = { version = "0.20.0", features = ["derive"] } 15 | 16 | [dev-dependencies] 17 | http-body-util = "0.1.0" 18 | tower = { version = "0.5.2", features = ["util"] } 19 | -------------------------------------------------------------------------------- /examples/versioning/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-versioning" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tracing = "0.1" 11 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 12 | 13 | [dev-dependencies] 14 | http-body-util = "0.1.0" 15 | tower = { version = "0.5.2", features = ["util"] } 16 | -------------------------------------------------------------------------------- /examples/websockets-http2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-websockets-http2" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["ws", "http2"] } 9 | axum-server = { version = "0.6", features = ["tls-rustls"] } 10 | tokio = { version = "1", features = ["full"] } 11 | tower-http = { version = "0.5.0", features = ["fs"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/websockets-http2/assets/index.html: -------------------------------------------------------------------------------- 1 |

Open this page in two windows and try sending some messages!

2 |
3 | 4 | 5 |
6 |
7 | 8 | -------------------------------------------------------------------------------- /examples/websockets-http2/assets/script.js: -------------------------------------------------------------------------------- 1 | const socket = new WebSocket('wss://localhost:3000/ws'); 2 | 3 | socket.addEventListener('message', e => { 4 | document.getElementById("messages").append(e.data, document.createElement("br")); 5 | }); 6 | 7 | const form = document.querySelector("form"); 8 | form.addEventListener("submit", () => { 9 | socket.send(form.elements.namedItem("content").value); 10 | form.elements.namedItem("content").value = ""; 11 | }); 12 | -------------------------------------------------------------------------------- /examples/websockets-http2/self_signed_certs/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL 3 | BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X 5 | DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR 6 | BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 7 | IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 8 | MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0 9 | daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4 10 | kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq 11 | dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT 12 | bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6 13 | J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK 14 | NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0 15 | yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W 16 | ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU 17 | XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg 18 | +MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9 19 | Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24 20 | fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr 21 | AopvZ09uEQ== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /examples/websockets-http2/self_signed_certs/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD 3 | BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS 4 | tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw 5 | RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l 6 | YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t 7 | HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV 8 | W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB 9 | 12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI 10 | +2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw 11 | zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt 12 | fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty 13 | RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT 14 | ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1 15 | myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ 16 | XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY 17 | 5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD 18 | ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD 19 | ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27 20 | k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7 21 | wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV 22 | 5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg 23 | 3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa 24 | Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C 25 | rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m 26 | y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW 27 | w37pCS7ykL+7gp7V0WShYsw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/websockets/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-websockets" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [[bin]] 8 | name = "example-websockets" 9 | path = "src/main.rs" 10 | 11 | [[bin]] 12 | name = "example-client" 13 | path = "src/client.rs" 14 | 15 | [dependencies] 16 | axum = { path = "../../axum", features = ["ws"] } 17 | axum-extra = { path = "../../axum-extra", features = ["typed-header"] } 18 | futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } 19 | headers = "0.4" 20 | tokio = { version = "1.0", features = ["full"] } 21 | tokio-tungstenite = "0.26.0" 22 | tower-http = { version = "0.6.1", features = ["fs", "trace"] } 23 | tracing = "0.1" 24 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 25 | -------------------------------------------------------------------------------- /examples/websockets/assets/index.html: -------------------------------------------------------------------------------- 1 | Open the console to see stuff, then refresh to initiate exchange. 2 | 3 | -------------------------------------------------------------------------------- /examples/websockets/assets/script.js: -------------------------------------------------------------------------------- 1 | const socket = new WebSocket('ws://localhost:3000/ws'); 2 | 3 | socket.addEventListener('open', function (event) { 4 | socket.send('Hello Server!'); 5 | }); 6 | 7 | socket.addEventListener('message', function (event) { 8 | console.log('Message from server ', event.data); 9 | }); 10 | 11 | 12 | setTimeout(() => { 13 | const obj = { hello: "world" }; 14 | const blob = new Blob([JSON.stringify(obj, null, 2)], { 15 | type: "application/json", 16 | }); 17 | console.log("Sending blob over websocket"); 18 | socket.send(blob); 19 | }, 1000); 20 | 21 | setTimeout(() => { 22 | socket.send('About done here...'); 23 | console.log("Sending close over websocket"); 24 | socket.close(3000, "Crash and Burn!"); 25 | }, 3000); --------------------------------------------------------------------------------