@rivetkit/engine 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +203 -0
- package/artifacts/errors/actor.destroyed_during_creation.json +5 -0
- package/artifacts/errors/actor.destroyed_while_waiting_for_ready.json +5 -0
- package/artifacts/errors/actor.duplicate_key.json +5 -0
- package/artifacts/errors/actor.empty_key.json +5 -0
- package/artifacts/errors/actor.input_too_large.json +5 -0
- package/artifacts/errors/actor.key_reserved_in_different_datacenter.json +5 -0
- package/artifacts/errors/actor.key_too_large.json +5 -0
- package/artifacts/errors/actor.kv_key_not_found.json +5 -0
- package/artifacts/errors/actor.namespace_not_found.json +5 -0
- package/artifacts/errors/actor.no_runners_available.json +5 -0
- package/artifacts/errors/actor.not_found.json +5 -0
- package/artifacts/errors/api.bad_request.json +5 -0
- package/artifacts/errors/api.forbidden.json +5 -0
- package/artifacts/errors/api.internal_error.json +5 -0
- package/artifacts/errors/api.not_found.json +5 -0
- package/artifacts/errors/api.rate_limited.json +5 -0
- package/artifacts/errors/api.unauthorized.json +5 -0
- package/artifacts/errors/datacenter.not_found.json +5 -0
- package/artifacts/errors/guard.actor_destroyed.json +5 -0
- package/artifacts/errors/guard.actor_not_found.json +5 -0
- package/artifacts/errors/guard.actor_ready_timeout.json +5 -0
- package/artifacts/errors/guard.connection_error.json +5 -0
- package/artifacts/errors/guard.http_request_build_failed.json +5 -0
- package/artifacts/errors/guard.missing_header.json +5 -0
- package/artifacts/errors/guard.must_use_regional_host.json +5 -0
- package/artifacts/errors/guard.no_route.json +5 -0
- package/artifacts/errors/guard.no_route_targets.json +5 -0
- package/artifacts/errors/guard.rate_limit.json +5 -0
- package/artifacts/errors/guard.request_build_error.json +5 -0
- package/artifacts/errors/guard.request_timeout.json +5 -0
- package/artifacts/errors/guard.retry_attempts_exceeded.json +5 -0
- package/artifacts/errors/guard.service_unavailable.json +5 -0
- package/artifacts/errors/guard.target_changed.json +5 -0
- package/artifacts/errors/guard.upstream_error.json +5 -0
- package/artifacts/errors/guard.uri_parse_error.json +5 -0
- package/artifacts/errors/guard.websocket_pending_limit_reached.json +5 -0
- package/artifacts/errors/guard.websocket_service_hibernate.json +5 -0
- package/artifacts/errors/guard.websocket_service_timeout.json +5 -0
- package/artifacts/errors/guard.websocket_service_unavailable.json +5 -0
- package/artifacts/errors/guard.wrong_addr_protocol.json +5 -0
- package/artifacts/errors/kv.leader_forwarding_failed.json +5 -0
- package/artifacts/errors/kv.no_leader_elected.json +5 -0
- package/artifacts/errors/kv.not_leader.json +5 -0
- package/artifacts/errors/kv.response_channel_closed.json +5 -0
- package/artifacts/errors/namespace.failed_to_create.json +5 -0
- package/artifacts/errors/namespace.invalid_name.json +5 -0
- package/artifacts/errors/namespace.invalid_update.json +5 -0
- package/artifacts/errors/namespace.name_not_unique.json +5 -0
- package/artifacts/errors/namespace.not_found.json +5 -0
- package/artifacts/errors/namespace.not_leader.json +5 -0
- package/artifacts/errors/runner.not_found.json +5 -0
- package/artifacts/errors/runner_config.invalid.json +5 -0
- package/artifacts/errors/runner_config.not_found.json +5 -0
- package/artifacts/errors/test.input_too_large.json +5 -0
- package/artifacts/errors/test.key_too_large.json +5 -0
- package/artifacts/errors/test.meta_error.json +5 -0
- package/artifacts/errors/test.not_found.json +5 -0
- package/artifacts/errors/test.simple_error.json +5 -0
- package/artifacts/errors/test.test_error.json +5 -0
- package/artifacts/errors/ups.publish_failed.json +5 -0
- package/artifacts/errors/ups.request_timeout.json +5 -0
- package/artifacts/errors/validation.invalid_input.json +5 -0
- package/artifacts/errors/validation.no_keys.json +5 -0
- package/artifacts/errors/validation.race_condition.json +5 -0
- package/artifacts/errors/validation.too_many_actor_ids.json +5 -0
- package/artifacts/errors/ws.connection_closed.json +5 -0
- package/artifacts/errors/ws.eviction.json +5 -0
- package/artifacts/errors/ws.invalid_initial_packet.json +5 -0
- package/artifacts/errors/ws.invalid_packet.json +5 -0
- package/artifacts/errors/ws.invalid_url.json +5 -0
- package/artifacts/errors/ws.timed_out_waiting_for_init.json +5 -0
- package/artifacts/openapi.json +1833 -0
- package/contrib-docs/ACTOR_KEY_RESERVATION.md +101 -0
- package/contrib-docs/API.md +11 -0
- package/contrib-docs/DOCKER.md +5 -0
- package/contrib-docs/ERRORS.md +13 -0
- package/contrib-docs/GUARD.md +76 -0
- package/contrib-docs/PEGBOARD_TUNNEL_RETRIES.md +83 -0
- package/contrib-docs/RUNNER_LIFECYCLE.md +172 -0
- package/contrib-docs/SDKS.md +9 -0
- package/contrib-docs/TEST_DEPENDENCIES.md +43 -0
- package/contrib-docs/design-choicse/EMBEDDED_KV.md +80 -0
- package/contrib-docs/operate/TRACING_RECONFIGURE.md +78 -0
- package/docker/dev/.gitattributes +1 -0
- package/docker/dev/README.md +74 -0
- package/docker/dev/clickhouse/client-config.xml +5 -0
- package/docker/dev/clickhouse/config.xml +52 -0
- package/docker/dev/clickhouse/init/01-create-otel-table.sql +107 -0
- package/docker/dev/clickhouse/users.xml +35 -0
- package/docker/dev/docker-compose.yml +217 -0
- package/docker/dev/grafana/dashboards/api.json +1240 -0
- package/docker/dev/grafana/dashboards/cache.json +1075 -0
- package/docker/dev/grafana/dashboards/futures.json +230 -0
- package/docker/dev/grafana/dashboards/gasoline.json +2477 -0
- package/docker/dev/grafana/dashboards/guard.json +1274 -0
- package/docker/dev/grafana/dashboards/tokio.json +1005 -0
- package/docker/dev/grafana/dashboards/traces.json +957 -0
- package/docker/dev/grafana/grafana.ini +14 -0
- package/docker/dev/grafana/provisioning/dashboards/dashboards.yaml +10 -0
- package/docker/dev/grafana/provisioning/datasources/datasources.yaml +30 -0
- package/docker/dev/otel-collector-client/config.yaml +39 -0
- package/docker/dev/otel-collector-server/config.yaml +72 -0
- package/docker/dev/postgres/init-db.sh +6 -0
- package/docker/dev/rivet-engine/config.jsonc +48 -0
- package/docker/dev/vector-client/vector.yaml +31 -0
- package/docker/dev/vector-server/vector.yaml +61 -0
- package/docker/dev-host/.gitattributes +1 -0
- package/docker/dev-host/README.md +74 -0
- package/docker/dev-host/clickhouse/client-config.xml +5 -0
- package/docker/dev-host/clickhouse/config.xml +52 -0
- package/docker/dev-host/clickhouse/init/01-create-otel-table.sql +107 -0
- package/docker/dev-host/clickhouse/users.xml +35 -0
- package/docker/dev-host/docker-compose.yml +190 -0
- package/docker/dev-host/grafana/dashboards/api.json +1240 -0
- package/docker/dev-host/grafana/dashboards/cache.json +1075 -0
- package/docker/dev-host/grafana/dashboards/futures.json +230 -0
- package/docker/dev-host/grafana/dashboards/gasoline.json +2477 -0
- package/docker/dev-host/grafana/dashboards/guard.json +1274 -0
- package/docker/dev-host/grafana/dashboards/tokio.json +1005 -0
- package/docker/dev-host/grafana/dashboards/traces.json +957 -0
- package/docker/dev-host/grafana/grafana.ini +14 -0
- package/docker/dev-host/grafana/provisioning/dashboards/dashboards.yaml +10 -0
- package/docker/dev-host/grafana/provisioning/datasources/datasources.yaml +30 -0
- package/docker/dev-host/otel-collector-client/config.yaml +39 -0
- package/docker/dev-host/otel-collector-server/config.yaml +72 -0
- package/docker/dev-host/postgres/init-db.sh +6 -0
- package/docker/dev-host/rivet-engine/config.jsonc +48 -0
- package/docker/dev-host/vector-client/vector.yaml +31 -0
- package/docker/dev-host/vector-server/vector.yaml +61 -0
- package/docker/dev-multidc/.gitattributes +1 -0
- package/docker/dev-multidc/README.md +86 -0
- package/docker/dev-multidc/core/clickhouse/client-config.xml +5 -0
- package/docker/dev-multidc/core/clickhouse/config.xml +52 -0
- package/docker/dev-multidc/core/clickhouse/init/01-create-otel-table.sql +107 -0
- package/docker/dev-multidc/core/clickhouse/users.xml +35 -0
- package/docker/dev-multidc/core/grafana/dashboards/api.json +1240 -0
- package/docker/dev-multidc/core/grafana/dashboards/cache.json +1075 -0
- package/docker/dev-multidc/core/grafana/dashboards/futures.json +230 -0
- package/docker/dev-multidc/core/grafana/dashboards/gasoline.json +2477 -0
- package/docker/dev-multidc/core/grafana/dashboards/guard.json +1274 -0
- package/docker/dev-multidc/core/grafana/dashboards/tokio.json +1005 -0
- package/docker/dev-multidc/core/grafana/dashboards/traces.json +957 -0
- package/docker/dev-multidc/core/grafana/grafana.ini +14 -0
- package/docker/dev-multidc/core/grafana/provisioning/dashboards/dashboards.yaml +10 -0
- package/docker/dev-multidc/core/grafana/provisioning/datasources/datasources.yaml +30 -0
- package/docker/dev-multidc/datacenters/dc-a/otel-collector-client/config.yaml +39 -0
- package/docker/dev-multidc/datacenters/dc-a/otel-collector-server/config.yaml +72 -0
- package/docker/dev-multidc/datacenters/dc-a/postgres/init-db.sh +6 -0
- package/docker/dev-multidc/datacenters/dc-a/rivet-engine/config.jsonc +62 -0
- package/docker/dev-multidc/datacenters/dc-a/vector-client/vector.yaml +31 -0
- package/docker/dev-multidc/datacenters/dc-a/vector-server/vector.yaml +61 -0
- package/docker/dev-multidc/datacenters/dc-b/otel-collector-client/config.yaml +39 -0
- package/docker/dev-multidc/datacenters/dc-b/otel-collector-server/config.yaml +72 -0
- package/docker/dev-multidc/datacenters/dc-b/postgres/init-db.sh +6 -0
- package/docker/dev-multidc/datacenters/dc-b/rivet-engine/config.jsonc +62 -0
- package/docker/dev-multidc/datacenters/dc-b/vector-client/vector.yaml +31 -0
- package/docker/dev-multidc/datacenters/dc-b/vector-server/vector.yaml +61 -0
- package/docker/dev-multidc/datacenters/dc-c/otel-collector-client/config.yaml +39 -0
- package/docker/dev-multidc/datacenters/dc-c/otel-collector-server/config.yaml +72 -0
- package/docker/dev-multidc/datacenters/dc-c/postgres/init-db.sh +6 -0
- package/docker/dev-multidc/datacenters/dc-c/rivet-engine/config.jsonc +62 -0
- package/docker/dev-multidc/datacenters/dc-c/vector-client/vector.yaml +31 -0
- package/docker/dev-multidc/datacenters/dc-c/vector-server/vector.yaml +61 -0
- package/docker/dev-multidc/docker-compose.yml +546 -0
- package/docker/dev-multidc-multinode/.gitattributes +1 -0
- package/docker/dev-multidc-multinode/README.md +86 -0
- package/docker/dev-multidc-multinode/core/clickhouse/client-config.xml +5 -0
- package/docker/dev-multidc-multinode/core/clickhouse/config.xml +52 -0
- package/docker/dev-multidc-multinode/core/clickhouse/init/01-create-otel-table.sql +107 -0
- package/docker/dev-multidc-multinode/core/clickhouse/users.xml +35 -0
- package/docker/dev-multidc-multinode/core/grafana/dashboards/api.json +1240 -0
- package/docker/dev-multidc-multinode/core/grafana/dashboards/cache.json +1075 -0
- package/docker/dev-multidc-multinode/core/grafana/dashboards/futures.json +230 -0
- package/docker/dev-multidc-multinode/core/grafana/dashboards/gasoline.json +2477 -0
- package/docker/dev-multidc-multinode/core/grafana/dashboards/guard.json +1274 -0
- package/docker/dev-multidc-multinode/core/grafana/dashboards/tokio.json +1005 -0
- package/docker/dev-multidc-multinode/core/grafana/dashboards/traces.json +957 -0
- package/docker/dev-multidc-multinode/core/grafana/grafana.ini +14 -0
- package/docker/dev-multidc-multinode/core/grafana/provisioning/dashboards/dashboards.yaml +10 -0
- package/docker/dev-multidc-multinode/core/grafana/provisioning/datasources/datasources.yaml +30 -0
- package/docker/dev-multidc-multinode/datacenters/dc-a/otel-collector-client/config.yaml +39 -0
- package/docker/dev-multidc-multinode/datacenters/dc-a/otel-collector-server/config.yaml +72 -0
- package/docker/dev-multidc-multinode/datacenters/dc-a/postgres/init-db.sh +6 -0
- package/docker/dev-multidc-multinode/datacenters/dc-a/rivet-engine/0/config.jsonc +62 -0
- package/docker/dev-multidc-multinode/datacenters/dc-a/rivet-engine/1/config.jsonc +62 -0
- package/docker/dev-multidc-multinode/datacenters/dc-a/rivet-engine/2/config.jsonc +62 -0
- package/docker/dev-multidc-multinode/datacenters/dc-a/vector-client/vector.yaml +31 -0
- package/docker/dev-multidc-multinode/datacenters/dc-a/vector-server/vector.yaml +61 -0
- package/docker/dev-multidc-multinode/datacenters/dc-b/otel-collector-client/config.yaml +39 -0
- package/docker/dev-multidc-multinode/datacenters/dc-b/otel-collector-server/config.yaml +72 -0
- package/docker/dev-multidc-multinode/datacenters/dc-b/postgres/init-db.sh +6 -0
- package/docker/dev-multidc-multinode/datacenters/dc-b/rivet-engine/0/config.jsonc +62 -0
- package/docker/dev-multidc-multinode/datacenters/dc-b/rivet-engine/1/config.jsonc +62 -0
- package/docker/dev-multidc-multinode/datacenters/dc-b/rivet-engine/2/config.jsonc +62 -0
- package/docker/dev-multidc-multinode/datacenters/dc-b/vector-client/vector.yaml +31 -0
- package/docker/dev-multidc-multinode/datacenters/dc-b/vector-server/vector.yaml +61 -0
- package/docker/dev-multidc-multinode/datacenters/dc-c/otel-collector-client/config.yaml +39 -0
- package/docker/dev-multidc-multinode/datacenters/dc-c/otel-collector-server/config.yaml +72 -0
- package/docker/dev-multidc-multinode/datacenters/dc-c/postgres/init-db.sh +6 -0
- package/docker/dev-multidc-multinode/datacenters/dc-c/rivet-engine/0/config.jsonc +62 -0
- package/docker/dev-multidc-multinode/datacenters/dc-c/rivet-engine/1/config.jsonc +62 -0
- package/docker/dev-multidc-multinode/datacenters/dc-c/rivet-engine/2/config.jsonc +62 -0
- package/docker/dev-multidc-multinode/datacenters/dc-c/vector-client/vector.yaml +31 -0
- package/docker/dev-multidc-multinode/datacenters/dc-c/vector-server/vector.yaml +61 -0
- package/docker/dev-multidc-multinode/docker-compose.yml +888 -0
- package/docker/dev-multinode/.gitattributes +1 -0
- package/docker/dev-multinode/README.md +74 -0
- package/docker/dev-multinode/clickhouse/client-config.xml +5 -0
- package/docker/dev-multinode/clickhouse/config.xml +52 -0
- package/docker/dev-multinode/clickhouse/init/01-create-otel-table.sql +107 -0
- package/docker/dev-multinode/clickhouse/users.xml +35 -0
- package/docker/dev-multinode/docker-compose.yml +323 -0
- package/docker/dev-multinode/grafana/dashboards/api.json +1240 -0
- package/docker/dev-multinode/grafana/dashboards/cache.json +1075 -0
- package/docker/dev-multinode/grafana/dashboards/futures.json +230 -0
- package/docker/dev-multinode/grafana/dashboards/gasoline.json +2477 -0
- package/docker/dev-multinode/grafana/dashboards/guard.json +1274 -0
- package/docker/dev-multinode/grafana/dashboards/tokio.json +1005 -0
- package/docker/dev-multinode/grafana/dashboards/traces.json +957 -0
- package/docker/dev-multinode/grafana/grafana.ini +14 -0
- package/docker/dev-multinode/grafana/provisioning/dashboards/dashboards.yaml +10 -0
- package/docker/dev-multinode/grafana/provisioning/datasources/datasources.yaml +30 -0
- package/docker/dev-multinode/otel-collector-client/config.yaml +39 -0
- package/docker/dev-multinode/otel-collector-server/config.yaml +72 -0
- package/docker/dev-multinode/postgres/init-db.sh +6 -0
- package/docker/dev-multinode/rivet-engine/0/config.jsonc +48 -0
- package/docker/dev-multinode/rivet-engine/1/config.jsonc +48 -0
- package/docker/dev-multinode/rivet-engine/2/config.jsonc +48 -0
- package/docker/dev-multinode/vector-client/vector.yaml +31 -0
- package/docker/dev-multinode/vector-server/vector.yaml +61 -0
- package/docker/engine/build.sh +67 -0
- package/docker/engine/linux-aarch64.Dockerfile +97 -0
- package/docker/engine/linux-x86_64.Dockerfile +93 -0
- package/docker/engine/macos-aarch64.Dockerfile +92 -0
- package/docker/engine/macos-x86_64.Dockerfile +92 -0
- package/docker/engine/windows.Dockerfile +80 -0
- package/docker/prod-file-system/.gitattributes +1 -0
- package/docker/prod-file-system/README.md +76 -0
- package/docker/prod-file-system/docker-compose.yml +41 -0
- package/docker/prod-file-system/rivet-engine/config.jsonc +2 -0
- package/docker/template/README.md +20 -0
- package/docker/template/grafana-dashboards/api.json +1240 -0
- package/docker/template/grafana-dashboards/cache.json +1075 -0
- package/docker/template/grafana-dashboards/futures.json +230 -0
- package/docker/template/grafana-dashboards/gasoline.json +2477 -0
- package/docker/template/grafana-dashboards/guard.json +1274 -0
- package/docker/template/grafana-dashboards/tokio.json +1005 -0
- package/docker/template/grafana-dashboards/traces.json +957 -0
- package/docker/template/node_modules/.bin/js-yaml +21 -0
- package/docker/template/node_modules/.bin/tsc +21 -0
- package/docker/template/node_modules/.bin/tsserver +21 -0
- package/docker/template/node_modules/.bin/tsx +21 -0
- package/docker/template/package.json +23 -0
- package/docker/template/src/config.ts +104 -0
- package/docker/template/src/context.ts +216 -0
- package/docker/template/src/docker-compose.ts +368 -0
- package/docker/template/src/git.ts +8 -0
- package/docker/template/src/main.ts +65 -0
- package/docker/template/src/readme.ts +98 -0
- package/docker/template/src/services/core/clickhouse.ts +223 -0
- package/docker/template/src/services/core/grafana.ts +115 -0
- package/docker/template/src/services/edge/otel-collector-client.ts +64 -0
- package/docker/template/src/services/edge/otel-collector-server.ts +107 -0
- package/docker/template/src/services/edge/postgres.ts +23 -0
- package/docker/template/src/services/edge/rivet-engine.ts +87 -0
- package/docker/template/src/services/edge/runner.ts +7 -0
- package/docker/template/src/services/edge/vector-client.ts +55 -0
- package/docker/template/src/services/edge/vector-server.ts +111 -0
- package/docker/template/src/utils.ts +19 -0
- package/docker/template/tsconfig.json +18 -0
- package/docker/universal/Dockerfile +133 -0
- package/package.json +10 -0
- package/packages/actor-kv/Cargo.toml +31 -0
- package/packages/actor-kv/src/entry.rs +147 -0
- package/packages/actor-kv/src/key.rs +81 -0
- package/packages/actor-kv/src/lib.rs +357 -0
- package/packages/actor-kv/src/utils.rs +101 -0
- package/packages/actor-kv/tests/kv_operations.rs +294 -0
- package/packages/actor-kv/tests/list_edge_cases.rs +370 -0
- package/packages/api-builder/Cargo.toml +36 -0
- package/packages/api-builder/src/context.rs +98 -0
- package/packages/api-builder/src/error_response.rs +116 -0
- package/packages/api-builder/src/errors.rs +29 -0
- package/packages/api-builder/src/extract.rs +137 -0
- package/packages/api-builder/src/global_context.rs +31 -0
- package/packages/api-builder/src/lib.rs +19 -0
- package/packages/api-builder/src/metrics.rs +29 -0
- package/packages/api-builder/src/middleware.rs +232 -0
- package/packages/api-builder/src/prelude.rs +18 -0
- package/packages/api-builder/src/request_ids.rs +17 -0
- package/packages/api-builder/src/router.rs +89 -0
- package/packages/api-builder/src/wrappers.rs +138 -0
- package/packages/api-builder/tests/basic.rs +215 -0
- package/packages/api-builder/tests/errors.rs +104 -0
- package/packages/api-peer/Cargo.toml +34 -0
- package/packages/api-peer/src/actors/create.rs +40 -0
- package/packages/api-peer/src/actors/delete.rs +87 -0
- package/packages/api-peer/src/actors/kv_get.rs +67 -0
- package/packages/api-peer/src/actors/list.rs +98 -0
- package/packages/api-peer/src/actors/list_names.rs +56 -0
- package/packages/api-peer/src/actors/mod.rs +5 -0
- package/packages/api-peer/src/internal.rs +186 -0
- package/packages/api-peer/src/lib.rs +28 -0
- package/packages/api-peer/src/namespaces.rs +122 -0
- package/packages/api-peer/src/router.rs +51 -0
- package/packages/api-peer/src/runner_configs.rs +160 -0
- package/packages/api-peer/src/runners.rs +109 -0
- package/packages/api-public/Cargo.toml +38 -0
- package/packages/api-public/build.rs +45 -0
- package/packages/api-public/src/actors/create.rs +99 -0
- package/packages/api-public/src/actors/delete.rs +86 -0
- package/packages/api-public/src/actors/get_or_create.rs +170 -0
- package/packages/api-public/src/actors/kv_get.rs +79 -0
- package/packages/api-public/src/actors/list.rs +241 -0
- package/packages/api-public/src/actors/list_names.rs +76 -0
- package/packages/api-public/src/actors/mod.rs +7 -0
- package/packages/api-public/src/actors/utils.rs +216 -0
- package/packages/api-public/src/ctx.rs +70 -0
- package/packages/api-public/src/datacenters.rs +43 -0
- package/packages/api-public/src/errors.rs +30 -0
- package/packages/api-public/src/health.rs +166 -0
- package/packages/api-public/src/lib.rs +13 -0
- package/packages/api-public/src/metadata.rs +24 -0
- package/packages/api-public/src/namespaces.rs +89 -0
- package/packages/api-public/src/router.rs +182 -0
- package/packages/api-public/src/runner_configs/delete.rs +99 -0
- package/packages/api-public/src/runner_configs/list.rs +98 -0
- package/packages/api-public/src/runner_configs/mod.rs +12 -0
- package/packages/api-public/src/runner_configs/refresh_metadata.rs +116 -0
- package/packages/api-public/src/runner_configs/serverless_health_check.rs +81 -0
- package/packages/api-public/src/runner_configs/upsert.rs +194 -0
- package/packages/api-public/src/runner_configs/utils.rs +217 -0
- package/packages/api-public/src/runners.rs +144 -0
- package/packages/api-public/src/ui.rs +44 -0
- package/packages/api-types/Cargo.toml +17 -0
- package/packages/api-types/src/actors/create.rs +29 -0
- package/packages/api-types/src/actors/list.rs +23 -0
- package/packages/api-types/src/actors/list_names.rs +24 -0
- package/packages/api-types/src/actors/mod.rs +3 -0
- package/packages/api-types/src/datacenters/list.rs +12 -0
- package/packages/api-types/src/datacenters/mod.rs +1 -0
- package/packages/api-types/src/lib.rs +6 -0
- package/packages/api-types/src/namespaces/list.rs +21 -0
- package/packages/api-types/src/namespaces/mod.rs +2 -0
- package/packages/api-types/src/namespaces/runner_configs.rs +55 -0
- package/packages/api-types/src/pagination.rs +8 -0
- package/packages/api-types/src/runner_configs/list.rs +29 -0
- package/packages/api-types/src/runner_configs/mod.rs +1 -0
- package/packages/api-types/src/runners/list.rs +25 -0
- package/packages/api-types/src/runners/mod.rs +1 -0
- package/packages/api-util/Cargo.toml +22 -0
- package/packages/api-util/src/errors.rs +9 -0
- package/packages/api-util/src/lib.rs +209 -0
- package/packages/bootstrap/Cargo.toml +19 -0
- package/packages/bootstrap/src/lib.rs +102 -0
- package/packages/cache/Cargo.toml +28 -0
- package/packages/cache/src/driver.rs +346 -0
- package/packages/cache/src/errors.rs +23 -0
- package/packages/cache/src/getter_ctx.rs +138 -0
- package/packages/cache/src/inner.rs +60 -0
- package/packages/cache/src/key.rs +101 -0
- package/packages/cache/src/lib.rs +19 -0
- package/packages/cache/src/metrics.rs +34 -0
- package/packages/cache/src/purge.rs +13 -0
- package/packages/cache/src/rate_limit.rs +109 -0
- package/packages/cache/src/req_config.rs +443 -0
- package/packages/cache/tests/integration.rs +582 -0
- package/packages/cache-purge/Cargo.toml +17 -0
- package/packages/cache-purge/src/lib.rs +44 -0
- package/packages/cache-result/Cargo.toml +9 -0
- package/packages/cache-result/src/lib.rs +61 -0
- package/packages/clickhouse-inserter/Cargo.toml +17 -0
- package/packages/clickhouse-inserter/src/error.rs +16 -0
- package/packages/clickhouse-inserter/src/lib.rs +179 -0
- package/packages/clickhouse-user-query/Cargo.toml +16 -0
- package/packages/clickhouse-user-query/examples/case_sensitivity_demo.rs +100 -0
- package/packages/clickhouse-user-query/examples/group_by_example.rs +53 -0
- package/packages/clickhouse-user-query/examples/string_contains_demo.rs +96 -0
- package/packages/clickhouse-user-query/src/builder.rs +445 -0
- package/packages/clickhouse-user-query/src/error.rs +37 -0
- package/packages/clickhouse-user-query/src/lib.rs +61 -0
- package/packages/clickhouse-user-query/src/query.rs +143 -0
- package/packages/clickhouse-user-query/src/schema.rs +78 -0
- package/packages/clickhouse-user-query/tests/builder_tests.rs +619 -0
- package/packages/clickhouse-user-query/tests/case_sensitivity_tests.rs +307 -0
- package/packages/clickhouse-user-query/tests/integration_tests.rs +540 -0
- package/packages/clickhouse-user-query/tests/query_tests.rs +263 -0
- package/packages/clickhouse-user-query/tests/schema_tests.rs +44 -0
- package/packages/config/Cargo.toml +20 -0
- package/packages/config/src/config/api_peer.rs +21 -0
- package/packages/config/src/config/api_public.rs +25 -0
- package/packages/config/src/config/auth.rs +10 -0
- package/packages/config/src/config/cache.rs +24 -0
- package/packages/config/src/config/clickhouse.rs +49 -0
- package/packages/config/src/config/db.rs +52 -0
- package/packages/config/src/config/guard.rs +42 -0
- package/packages/config/src/config/logs.rs +10 -0
- package/packages/config/src/config/mod.rs +240 -0
- package/packages/config/src/config/pegboard.rs +94 -0
- package/packages/config/src/config/pubsub.rs +90 -0
- package/packages/config/src/config/runtime.rs +31 -0
- package/packages/config/src/config/telemetry.rs +14 -0
- package/packages/config/src/config/topology.rs +123 -0
- package/packages/config/src/config/vector.rs +18 -0
- package/packages/config/src/defaults.rs +11 -0
- package/packages/config/src/lib.rs +135 -0
- package/packages/config/src/paths.rs +25 -0
- package/packages/config/src/secret.rs +72 -0
- package/packages/dump-openapi/Cargo.toml +11 -0
- package/packages/dump-openapi/build.rs +26 -0
- package/packages/dump-openapi/src/lib.rs +1 -0
- package/packages/engine/Cargo.toml +74 -0
- package/packages/engine/src/commands/config.rs +18 -0
- package/packages/engine/src/commands/db/mod.rs +80 -0
- package/packages/engine/src/commands/mod.rs +7 -0
- package/packages/engine/src/commands/start.rs +143 -0
- package/packages/engine/src/commands/tracing.rs +86 -0
- package/packages/engine/src/commands/udb/cli.rs +562 -0
- package/packages/engine/src/commands/udb/mod.rs +132 -0
- package/packages/engine/src/commands/udb_keys.rs +200 -0
- package/packages/engine/src/commands/wf/mod.rs +125 -0
- package/packages/engine/src/commands/wf/signal.rs +80 -0
- package/packages/engine/src/keys.rs +47 -0
- package/packages/engine/src/lib.rs +55 -0
- package/packages/engine/src/main.rs +60 -0
- package/packages/engine/src/run_config.rs +53 -0
- package/packages/engine/src/util/db.rs +99 -0
- package/packages/engine/src/util/format.rs +69 -0
- package/packages/engine/src/util/mod.rs +13 -0
- package/packages/engine/src/util/udb.rs +687 -0
- package/packages/engine/src/util/wf/mod.rs +649 -0
- package/packages/engine/src/util/wf/signal.rs +126 -0
- package/packages/engine/tests/actors_create.rs +524 -0
- package/packages/engine/tests/actors_delete.rs +243 -0
- package/packages/engine/tests/actors_general.rs +191 -0
- package/packages/engine/tests/actors_get.rs +230 -0
- package/packages/engine/tests/actors_get_by_id.rs +170 -0
- package/packages/engine/tests/actors_get_or_create.rs +294 -0
- package/packages/engine/tests/actors_get_or_create_by_id.rs +147 -0
- package/packages/engine/tests/actors_lifecycle.rs +165 -0
- package/packages/engine/tests/actors_list.rs +798 -0
- package/packages/engine/tests/actors_list_names.rs +353 -0
- package/packages/engine/tests/common/actors.rs +554 -0
- package/packages/engine/tests/common/ctx.rs +201 -0
- package/packages/engine/tests/common/mod.rs +30 -0
- package/packages/engine/tests/common/ns.rs +36 -0
- package/packages/engine/tests/common/runner.rs +134 -0
- package/packages/engine/tests/common/test_helpers.rs +226 -0
- package/packages/engine/tests/runners_dupe_key.rs +27 -0
- package/packages/engine/tests/runners_version.rs +50 -0
- package/packages/env/Cargo.toml +14 -0
- package/packages/env/build.rs +8 -0
- package/packages/env/src/lib.rs +30 -0
- package/packages/epoxy/Cargo.toml +45 -0
- package/packages/epoxy/README.md +158 -0
- package/packages/epoxy/spec/KEYS.md +33 -0
- package/packages/epoxy/spec/PROPOSAL.md +125 -0
- package/packages/epoxy/spec/RECONFIGURE.md +40 -0
- package/packages/epoxy/src/consts.rs +42 -0
- package/packages/epoxy/src/errors.rs +21 -0
- package/packages/epoxy/src/http_client.rs +192 -0
- package/packages/epoxy/src/http_routes.rs +34 -0
- package/packages/epoxy/src/keys/keys.rs +99 -0
- package/packages/epoxy/src/keys/mod.rs +9 -0
- package/packages/epoxy/src/keys/replica.rs +283 -0
- package/packages/epoxy/src/lib.rs +25 -0
- package/packages/epoxy/src/ops/explicit_prepare.rs +342 -0
- package/packages/epoxy/src/ops/kv/get_local.rs +44 -0
- package/packages/epoxy/src/ops/kv/get_optimistic.rs +150 -0
- package/packages/epoxy/src/ops/kv/mod.rs +3 -0
- package/packages/epoxy/src/ops/kv/purge_local.rs +28 -0
- package/packages/epoxy/src/ops/mod.rs +4 -0
- package/packages/epoxy/src/ops/propose.rs +352 -0
- package/packages/epoxy/src/ops/read_cluster_config.rs +24 -0
- package/packages/epoxy/src/replica/ballot.rs +130 -0
- package/packages/epoxy/src/replica/commit_kv.rs +80 -0
- package/packages/epoxy/src/replica/decide_path.rs +51 -0
- package/packages/epoxy/src/replica/lead_consensus.rs +65 -0
- package/packages/epoxy/src/replica/log.rs +84 -0
- package/packages/epoxy/src/replica/message_request.rs +166 -0
- package/packages/epoxy/src/replica/messages/accept.rs +50 -0
- package/packages/epoxy/src/replica/messages/accepted.rs +35 -0
- package/packages/epoxy/src/replica/messages/commit.rs +46 -0
- package/packages/epoxy/src/replica/messages/committed.rs +41 -0
- package/packages/epoxy/src/replica/messages/download_instances.rs +69 -0
- package/packages/epoxy/src/replica/messages/mod.rs +15 -0
- package/packages/epoxy/src/replica/messages/pre_accept.rs +69 -0
- package/packages/epoxy/src/replica/messages/prepare.rs +89 -0
- package/packages/epoxy/src/replica/mod.rs +11 -0
- package/packages/epoxy/src/replica/update_config.rs +24 -0
- package/packages/epoxy/src/replica/utils.rs +111 -0
- package/packages/epoxy/src/types.rs +117 -0
- package/packages/epoxy/src/utils.rs +65 -0
- package/packages/epoxy/src/workflows/coordinator/mod.rs +121 -0
- package/packages/epoxy/src/workflows/coordinator/reconfigure.rs +291 -0
- package/packages/epoxy/src/workflows/coordinator/replica_status_change.rs +193 -0
- package/packages/epoxy/src/workflows/mod.rs +3 -0
- package/packages/epoxy/src/workflows/purger.rs +81 -0
- package/packages/epoxy/src/workflows/replica/mod.rs +39 -0
- package/packages/epoxy/src/workflows/replica/setup.rs +823 -0
- package/packages/epoxy/tests/common/api.rs +25 -0
- package/packages/epoxy/tests/common/mod.rs +301 -0
- package/packages/epoxy/tests/common/utils.rs +23 -0
- package/packages/epoxy/tests/kv.rs +187 -0
- package/packages/epoxy/tests/kv_get_optimistic.rs +179 -0
- package/packages/epoxy/tests/proposal.rs +38 -0
- package/packages/epoxy/tests/reconfigure.rs +618 -0
- package/packages/error/Cargo.toml +16 -0
- package/packages/error/src/error.rs +90 -0
- package/packages/error/src/lib.rs +13 -0
- package/packages/error/src/schema.rs +86 -0
- package/packages/error/tests/basic.rs +338 -0
- package/packages/error-macros/Cargo.toml +19 -0
- package/packages/error-macros/src/lib.rs +632 -0
- package/packages/gasoline/Cargo.toml +55 -0
- package/packages/gasoline/src/activity.rs +24 -0
- package/packages/gasoline/src/builder/common/message.rs +104 -0
- package/packages/gasoline/src/builder/common/mod.rs +5 -0
- package/packages/gasoline/src/builder/common/signal.rs +193 -0
- package/packages/gasoline/src/builder/common/workflow.rs +196 -0
- package/packages/gasoline/src/builder/mod.rs +60 -0
- package/packages/gasoline/src/builder/workflow/message.rs +147 -0
- package/packages/gasoline/src/builder/workflow/mod.rs +5 -0
- package/packages/gasoline/src/builder/workflow/signal.rs +227 -0
- package/packages/gasoline/src/builder/workflow/sub_workflow.rs +318 -0
- package/packages/gasoline/src/ctx/activity.rs +192 -0
- package/packages/gasoline/src/ctx/common.rs +153 -0
- package/packages/gasoline/src/ctx/listen.rs +121 -0
- package/packages/gasoline/src/ctx/message.rs +346 -0
- package/packages/gasoline/src/ctx/mod.rs +18 -0
- package/packages/gasoline/src/ctx/operation.rs +191 -0
- package/packages/gasoline/src/ctx/standalone.rs +228 -0
- package/packages/gasoline/src/ctx/test.rs +251 -0
- package/packages/gasoline/src/ctx/versioned_workflow.rs +198 -0
- package/packages/gasoline/src/ctx/workflow.rs +1459 -0
- package/packages/gasoline/src/db/debug.rs +199 -0
- package/packages/gasoline/src/db/kv/debug.rs +1493 -0
- package/packages/gasoline/src/db/kv/keys/history.rs +1780 -0
- package/packages/gasoline/src/db/kv/keys/metric.rs +170 -0
- package/packages/gasoline/src/db/kv/keys/mod.rs +6 -0
- package/packages/gasoline/src/db/kv/keys/signal.rs +401 -0
- package/packages/gasoline/src/db/kv/keys/wake.rs +310 -0
- package/packages/gasoline/src/db/kv/keys/worker.rs +185 -0
- package/packages/gasoline/src/db/kv/keys/workflow.rs +1175 -0
- package/packages/gasoline/src/db/kv/mod.rs +3044 -0
- package/packages/gasoline/src/db/kv/subjects.rs +13 -0
- package/packages/gasoline/src/db/kv/system.rs +33 -0
- package/packages/gasoline/src/db/mod.rs +344 -0
- package/packages/gasoline/src/error.rs +258 -0
- package/packages/gasoline/src/executable.rs +186 -0
- package/packages/gasoline/src/history/cursor.rs +779 -0
- package/packages/gasoline/src/history/event.rs +210 -0
- package/packages/gasoline/src/history/location.rs +185 -0
- package/packages/gasoline/src/history/mod.rs +8 -0
- package/packages/gasoline/src/history/removed.rs +94 -0
- package/packages/gasoline/src/lib.rs +18 -0
- package/packages/gasoline/src/listen.rs +24 -0
- package/packages/gasoline/src/message.rs +103 -0
- package/packages/gasoline/src/metrics.rs +156 -0
- package/packages/gasoline/src/operation.rs +21 -0
- package/packages/gasoline/src/prelude.rs +53 -0
- package/packages/gasoline/src/registry.rs +111 -0
- package/packages/gasoline/src/signal.rs +117 -0
- package/packages/gasoline/src/stub.rs +118 -0
- package/packages/gasoline/src/utils/mod.rs +20 -0
- package/packages/gasoline/src/utils/tags.rs +82 -0
- package/packages/gasoline/src/utils/time.rs +85 -0
- package/packages/gasoline/src/worker.rs +336 -0
- package/packages/gasoline/src/workflow.rs +64 -0
- package/packages/gasoline/tests/activity_ctx.rs +83 -0
- package/packages/gasoline/tests/workflow_ctx.rs +288 -0
- package/packages/gasoline/tests/workflows/activity_test.rs +33 -0
- package/packages/gasoline/tests/workflows/basic.rs +13 -0
- package/packages/gasoline/tests/workflows/eviction_test.rs +45 -0
- package/packages/gasoline/tests/workflows/listen_timeout.rs +27 -0
- package/packages/gasoline/tests/workflows/loop_test.rs +30 -0
- package/packages/gasoline/tests/workflows/mod.rs +11 -0
- package/packages/gasoline/tests/workflows/properties_test.rs +56 -0
- package/packages/gasoline/tests/workflows/signal_test.rs +24 -0
- package/packages/gasoline/tests/workflows/sleep_test.rs +15 -0
- package/packages/gasoline/tests/workflows/state_test.rs +69 -0
- package/packages/gasoline/tests/workflows/sub_test.rs +21 -0
- package/packages/gasoline/tests/workflows/tags_test.rs +40 -0
- package/packages/gasoline-macros/Cargo.toml +14 -0
- package/packages/gasoline-macros/src/lib.rs +527 -0
- package/packages/guard/Cargo.toml +56 -0
- package/packages/guard/src/cache/actor.rs +43 -0
- package/packages/guard/src/cache/mod.rs +69 -0
- package/packages/guard/src/errors.rs +62 -0
- package/packages/guard/src/lib.rs +58 -0
- package/packages/guard/src/middleware.rs +42 -0
- package/packages/guard/src/routing/api_public.rs +70 -0
- package/packages/guard/src/routing/mod.rs +245 -0
- package/packages/guard/src/routing/pegboard_gateway.rs +260 -0
- package/packages/guard/src/routing/runner.rs +129 -0
- package/packages/guard/src/shared_state.rs +31 -0
- package/packages/guard/src/tls.rs +224 -0
- package/packages/guard/tests/parse_actor_path.rs +240 -0
- package/packages/guard-core/Cargo.toml +58 -0
- package/packages/guard-core/src/analytics.rs +46 -0
- package/packages/guard-core/src/cert_resolver.rs +61 -0
- package/packages/guard-core/src/custom_serve.rs +52 -0
- package/packages/guard-core/src/errors.rs +113 -0
- package/packages/guard-core/src/lib.rs +25 -0
- package/packages/guard-core/src/metrics.rs +64 -0
- package/packages/guard-core/src/proxy_service.rs +2699 -0
- package/packages/guard-core/src/request_context.rs +184 -0
- package/packages/guard-core/src/server.rs +289 -0
- package/packages/guard-core/src/task_group.rs +57 -0
- package/packages/guard-core/src/types.rs +19 -0
- package/packages/guard-core/src/websocket_handle.rs +47 -0
- package/packages/guard-core/tests/common/mod.rs +678 -0
- package/packages/guard-core/tests/custom_serve.rs +392 -0
- package/packages/guard-core/tests/fixtures/tls/README.md +6 -0
- package/packages/guard-core/tests/fixtures/tls/api_cert.pem +51 -0
- package/packages/guard-core/tests/fixtures/tls/api_key.pem +7 -0
- package/packages/guard-core/tests/fixtures/tls/job_cert.pem +55 -0
- package/packages/guard-core/tests/fixtures/tls/job_key.pem +7 -0
- package/packages/guard-core/tests/https.rs +400 -0
- package/packages/guard-core/tests/metrics.rs +247 -0
- package/packages/guard-core/tests/proxy.rs +842 -0
- package/packages/guard-core/tests/simple_websocket.rs +258 -0
- package/packages/guard-core/tests/simple_websocket_echo.rs +318 -0
- package/packages/guard-core/tests/simple_websocket_test2.rs +120 -0
- package/packages/guard-core/tests/streaming_response.rs +265 -0
- package/packages/guard-core/tests/websocket.rs +1113 -0
- package/packages/internal/Cargo.toml +13 -0
- package/packages/internal/README.md +1 -0
- package/packages/internal/src/lib.rs +1 -0
- package/packages/internal/src/ops/bump_serverless_autoscaler_global.rs +64 -0
- package/packages/internal/src/ops/cache/mod.rs +1 -0
- package/packages/internal/src/ops/cache/purge_global.rs +81 -0
- package/packages/internal/src/ops/mod.rs +2 -0
- package/packages/logs/Cargo.toml +20 -0
- package/packages/logs/src/lib.rs +9 -0
- package/packages/logs/src/unix.rs +201 -0
- package/packages/logs/src/windows.rs +240 -0
- package/packages/metrics/Cargo.toml +20 -0
- package/packages/metrics/src/buckets.rs +32 -0
- package/packages/metrics/src/lib.rs +8 -0
- package/packages/metrics/src/providers.rs +183 -0
- package/packages/namespace/Cargo.toml +28 -0
- package/packages/namespace/src/errors.rs +39 -0
- package/packages/namespace/src/keys/mod.rs +190 -0
- package/packages/namespace/src/keys/runner_config.rs +211 -0
- package/packages/namespace/src/lib.rs +16 -0
- package/packages/namespace/src/ops/get_global.rs +58 -0
- package/packages/namespace/src/ops/get_local.rs +69 -0
- package/packages/namespace/src/ops/list.rs +69 -0
- package/packages/namespace/src/ops/mod.rs +6 -0
- package/packages/namespace/src/ops/resolve_for_name_global.rs +57 -0
- package/packages/namespace/src/ops/resolve_for_name_local.rs +41 -0
- package/packages/namespace/src/ops/runner_config/delete.rs +53 -0
- package/packages/namespace/src/ops/runner_config/get.rs +94 -0
- package/packages/namespace/src/ops/runner_config/list.rs +94 -0
- package/packages/namespace/src/ops/runner_config/mod.rs +4 -0
- package/packages/namespace/src/ops/runner_config/upsert.rs +148 -0
- package/packages/namespace/src/utils.rs +11 -0
- package/packages/namespace/src/workflows/mod.rs +1 -0
- package/packages/namespace/src/workflows/namespace.rs +178 -0
- package/packages/pegboard/Cargo.toml +34 -0
- package/packages/pegboard/src/errors.rs +76 -0
- package/packages/pegboard/src/keys/actor.rs +443 -0
- package/packages/pegboard/src/keys/epoxy/mod.rs +1 -0
- package/packages/pegboard/src/keys/epoxy/ns.rs +68 -0
- package/packages/pegboard/src/keys/hibernating_request.rs +72 -0
- package/packages/pegboard/src/keys/mod.rs +15 -0
- package/packages/pegboard/src/keys/ns.rs +1367 -0
- package/packages/pegboard/src/keys/runner.rs +818 -0
- package/packages/pegboard/src/lib.rs +19 -0
- package/packages/pegboard/src/metrics.rs +19 -0
- package/packages/pegboard/src/ops/actor/create.rs +159 -0
- package/packages/pegboard/src/ops/actor/get.rs +100 -0
- package/packages/pegboard/src/ops/actor/get_for_gateway.rs +70 -0
- package/packages/pegboard/src/ops/actor/get_for_key.rs +93 -0
- package/packages/pegboard/src/ops/actor/get_reservation_for_key.rs +46 -0
- package/packages/pegboard/src/ops/actor/get_runner.rs +64 -0
- package/packages/pegboard/src/ops/actor/hibernating_request/delete.rs +41 -0
- package/packages/pegboard/src/ops/actor/hibernating_request/list.rs +65 -0
- package/packages/pegboard/src/ops/actor/hibernating_request/mod.rs +3 -0
- package/packages/pegboard/src/ops/actor/hibernating_request/upsert.rs +51 -0
- package/packages/pegboard/src/ops/actor/list_for_ns.rs +207 -0
- package/packages/pegboard/src/ops/actor/list_names.rs +62 -0
- package/packages/pegboard/src/ops/actor/mod.rs +9 -0
- package/packages/pegboard/src/ops/mod.rs +2 -0
- package/packages/pegboard/src/ops/runner/find_dc_with_runner.rs +222 -0
- package/packages/pegboard/src/ops/runner/get.rs +143 -0
- package/packages/pegboard/src/ops/runner/get_by_key.rs +51 -0
- package/packages/pegboard/src/ops/runner/list_for_ns.rs +209 -0
- package/packages/pegboard/src/ops/runner/list_names.rs +60 -0
- package/packages/pegboard/src/ops/runner/mod.rs +6 -0
- package/packages/pegboard/src/ops/runner/update_alloc_idx.rs +208 -0
- package/packages/pegboard/src/pubsub_subjects.rs +80 -0
- package/packages/pegboard/src/utils.rs +29 -0
- package/packages/pegboard/src/workflows/actor/destroy.rs +264 -0
- package/packages/pegboard/src/workflows/actor/keys.rs +283 -0
- package/packages/pegboard/src/workflows/actor/mod.rs +804 -0
- package/packages/pegboard/src/workflows/actor/runtime.rs +909 -0
- package/packages/pegboard/src/workflows/actor/setup.rs +175 -0
- package/packages/pegboard/src/workflows/mod.rs +2 -0
- package/packages/pegboard/src/workflows/runner.rs +1182 -0
- package/packages/pegboard-gateway/Cargo.toml +35 -0
- package/packages/pegboard-gateway/src/keepalive_task.rs +61 -0
- package/packages/pegboard-gateway/src/lib.rs +698 -0
- package/packages/pegboard-gateway/src/metrics.rs +14 -0
- package/packages/pegboard-gateway/src/ping_task.rs +23 -0
- package/packages/pegboard-gateway/src/shared_state.rs +588 -0
- package/packages/pegboard-gateway/src/tunnel_to_ws_task.rs +85 -0
- package/packages/pegboard-gateway/src/ws_to_tunnel_task.rs +65 -0
- package/packages/pegboard-runner/Cargo.toml +38 -0
- package/packages/pegboard-runner/src/conn.rs +183 -0
- package/packages/pegboard-runner/src/errors.rs +33 -0
- package/packages/pegboard-runner/src/lib.rs +249 -0
- package/packages/pegboard-runner/src/ping_task.rs +66 -0
- package/packages/pegboard-runner/src/tunnel_to_ws_task.rs +137 -0
- package/packages/pegboard-runner/src/utils.rs +40 -0
- package/packages/pegboard-runner/src/ws_to_tunnel_task.rs +443 -0
- package/packages/pegboard-serverless/Cargo.toml +25 -0
- package/packages/pegboard-serverless/src/lib.rs +523 -0
- package/packages/pools/Cargo.toml +38 -0
- package/packages/pools/src/db/clickhouse.rs +37 -0
- package/packages/pools/src/db/mod.rs +3 -0
- package/packages/pools/src/db/udb.rs +37 -0
- package/packages/pools/src/db/ups.rs +91 -0
- package/packages/pools/src/error.rs +41 -0
- package/packages/pools/src/lib.rs +16 -0
- package/packages/pools/src/metrics.rs +32 -0
- package/packages/pools/src/pools.rs +114 -0
- package/packages/pools/src/prelude.rs +3 -0
- package/packages/pools/src/reqwest.rs +25 -0
- package/packages/runtime/Cargo.lock +1394 -0
- package/packages/runtime/Cargo.toml +29 -0
- package/packages/runtime/src/lib.rs +154 -0
- package/packages/runtime/src/metrics.rs +34 -0
- package/packages/runtime/src/term_signal.rs +113 -0
- package/packages/runtime/src/traces.rs +158 -0
- package/packages/service-manager/Cargo.toml +19 -0
- package/packages/service-manager/src/lib.rs +359 -0
- package/packages/telemetry/Cargo.toml +13 -0
- package/packages/telemetry/README.md +12 -0
- package/packages/telemetry/src/lib.rs +39 -0
- package/packages/test-deps/Cargo.toml +19 -0
- package/packages/test-deps/src/datacenter.rs +109 -0
- package/packages/test-deps/src/lib.rs +131 -0
- package/packages/test-deps-docker/Cargo.toml +14 -0
- package/packages/test-deps-docker/src/database.rs +135 -0
- package/packages/test-deps-docker/src/lib.rs +280 -0
- package/packages/test-deps-docker/src/pubsub.rs +76 -0
- package/packages/tracing-reconfigure/Cargo.toml +18 -0
- package/packages/tracing-reconfigure/src/lib.rs +78 -0
- package/packages/tracing-utils/Cargo.toml +12 -0
- package/packages/tracing-utils/src/lib.rs +91 -0
- package/packages/types/Cargo.toml +19 -0
- package/packages/types/README.md +3 -0
- package/packages/types/src/actors.rs +79 -0
- package/packages/types/src/datacenters.rs +10 -0
- package/packages/types/src/keys/mod.rs +2 -0
- package/packages/types/src/keys/namespace/mod.rs +1 -0
- package/packages/types/src/keys/namespace/runner_config.rs +28 -0
- package/packages/types/src/keys/pegboard/mod.rs +7 -0
- package/packages/types/src/keys/pegboard/ns.rs +109 -0
- package/packages/types/src/lib.rs +7 -0
- package/packages/types/src/msgs/mod.rs +1 -0
- package/packages/types/src/msgs/pegboard.rs +5 -0
- package/packages/types/src/namespaces.rs +10 -0
- package/packages/types/src/runner_configs.rs +96 -0
- package/packages/types/src/runners.rs +24 -0
- package/packages/universaldb/Cargo.toml +32 -0
- package/packages/universaldb/src/atomic.rs +190 -0
- package/packages/universaldb/src/database.rs +56 -0
- package/packages/universaldb/src/driver/mod.rs +87 -0
- package/packages/universaldb/src/driver/postgres/database.rs +244 -0
- package/packages/universaldb/src/driver/postgres/mod.rs +5 -0
- package/packages/universaldb/src/driver/postgres/transaction.rs +337 -0
- package/packages/universaldb/src/driver/postgres/transaction_task.rs +538 -0
- package/packages/universaldb/src/driver/rocksdb/database.rs +129 -0
- package/packages/universaldb/src/driver/rocksdb/mod.rs +6 -0
- package/packages/universaldb/src/driver/rocksdb/transaction.rs +354 -0
- package/packages/universaldb/src/driver/rocksdb/transaction_conflict_tracker.rs +109 -0
- package/packages/universaldb/src/driver/rocksdb/transaction_task.rs +526 -0
- package/packages/universaldb/src/error.rs +30 -0
- package/packages/universaldb/src/key_selector.rs +97 -0
- package/packages/universaldb/src/lib.rs +24 -0
- package/packages/universaldb/src/metrics.rs +27 -0
- package/packages/universaldb/src/options.rs +284 -0
- package/packages/universaldb/src/prelude.rs +8 -0
- package/packages/universaldb/src/range_option.rs +159 -0
- package/packages/universaldb/src/transaction.rs +378 -0
- package/packages/universaldb/src/tx_ops.rs +425 -0
- package/packages/universaldb/src/utils/cherry_pick.rs +88 -0
- package/packages/universaldb/src/utils/codes.rs +9 -0
- package/packages/universaldb/src/utils/ext.rs +58 -0
- package/packages/universaldb/src/utils/formal_key.rs +23 -0
- package/packages/universaldb/src/utils/keys.rs +134 -0
- package/packages/universaldb/src/utils/mod.rs +92 -0
- package/packages/universaldb/src/utils/subspace.rs +95 -0
- package/packages/universaldb/src/value.rs +159 -0
- package/packages/universaldb/src/versionstamp.rs +173 -0
- package/packages/universaldb/tests/integration.rs +2733 -0
- package/packages/universaldb/tests/integration_gas.rs +264 -0
- package/packages/universaldb/tests/rocksdb.rs +141 -0
- package/packages/universaldb/tests/versionstamp.rs +184 -0
- package/packages/universalpubsub/Cargo.toml +38 -0
- package/packages/universalpubsub/benches/simple.rs +932 -0
- package/packages/universalpubsub/src/chunking.rs +226 -0
- package/packages/universalpubsub/src/driver/memory/mod.rs +91 -0
- package/packages/universalpubsub/src/driver/mod.rs +58 -0
- package/packages/universalpubsub/src/driver/nats/mod.rs +79 -0
- package/packages/universalpubsub/src/driver/postgres/mod.rs +443 -0
- package/packages/universalpubsub/src/errors.rs +11 -0
- package/packages/universalpubsub/src/lib.rs +7 -0
- package/packages/universalpubsub/src/pubsub.rs +415 -0
- package/packages/universalpubsub/tests/integration.rs +441 -0
- package/packages/universalpubsub/tests/reconnect.rs +359 -0
- package/packages/util/Cargo.toml +41 -0
- package/packages/util/build.rs +13 -0
- package/packages/util/src/backoff.rs +109 -0
- package/packages/util/src/billing.rs +3 -0
- package/packages/util/src/build_meta.rs +33 -0
- package/packages/util/src/check.rs +222 -0
- package/packages/util/src/duration.rs +77 -0
- package/packages/util/src/faker.rs +81 -0
- package/packages/util/src/format.rs +185 -0
- package/packages/util/src/future.rs +1 -0
- package/packages/util/src/geo.rs +5 -0
- package/packages/util/src/lib.rs +40 -0
- package/packages/util/src/math.rs +35 -0
- package/packages/util/src/req.rs +41 -0
- package/packages/util/src/serde.rs +516 -0
- package/packages/util/src/size.rs +27 -0
- package/packages/util/src/sort.rs +13 -0
- package/packages/util/src/timestamp.rs +58 -0
- package/packages/util/src/url.rs +3 -0
- package/packages/util-id/Cargo.toml +13 -0
- package/packages/util-id/src/lib.rs +329 -0
- package/packages/workflow-worker/Cargo.toml +16 -0
- package/packages/workflow-worker/src/lib.rs +15 -0
- package/sdks/api/fern/fern.config.json +4 -0
- package/sdks/api/fern/generators.yml +25 -0
- package/sdks/go/api-full/client/client.go +459 -0
- package/sdks/go/api-full/client/client_test.go +43 -0
- package/sdks/go/api-full/client/options.go +39 -0
- package/sdks/go/api-full/core/client_option.go +44 -0
- package/sdks/go/api-full/core/core.go +220 -0
- package/sdks/go/api-full/core/core_test.go +219 -0
- package/sdks/go/api-full/core/stringer.go +13 -0
- package/sdks/go/api-full/datacenters/client.go +50 -0
- package/sdks/go/api-full/go.mod +8 -0
- package/sdks/go/api-full/go.sum +12 -0
- package/sdks/go/api-full/health/client.go +50 -0
- package/sdks/go/api-full/namespaces/client.go +92 -0
- package/sdks/go/api-full/namespaces.go +15 -0
- package/sdks/go/api-full/pointer.go +103 -0
- package/sdks/go/api-full/runners/client.go +111 -0
- package/sdks/go/api-full/runners.go +18 -0
- package/sdks/go/api-full/types.go +1638 -0
- package/sdks/rust/api-full/.openapi-generator/FILES +28 -0
- package/sdks/rust/api-full/.openapi-generator/VERSION +1 -0
- package/sdks/rust/api-full/.openapi-generator-ignore +23 -0
- package/sdks/rust/api-full/.travis.yml +1 -0
- package/sdks/rust/api-full/Cargo.toml +15 -0
- package/sdks/rust/api-full/README.md +53 -0
- package/sdks/rust/api-full/docs/Actor.md +19 -0
- package/sdks/rust/api-full/docs/ActorLifecycle.md +12 -0
- package/sdks/rust/api-full/docs/ActorsApi.md +37 -0
- package/sdks/rust/api-full/docs/ActorsCreateRequest.md +13 -0
- package/sdks/rust/api-full/docs/ActorsCreateResponse.md +11 -0
- package/sdks/rust/api-full/docs/Namespace.md +14 -0
- package/sdks/rust/api-full/docs/NamespacesCreateRequest.md +12 -0
- package/sdks/rust/api-full/docs/NamespacesCreateResponse.md +11 -0
- package/sdks/rust/api-full/docs/NsApi.md +37 -0
- package/sdks/rust/api-full/git_push.sh +57 -0
- package/sdks/rust/api-full/rust/.openapi-generator/FILES +140 -0
- package/sdks/rust/api-full/rust/.openapi-generator/VERSION +1 -0
- package/sdks/rust/api-full/rust/.openapi-generator-ignore +23 -0
- package/sdks/rust/api-full/rust/.travis.yml +1 -0
- package/sdks/rust/api-full/rust/Cargo.toml +15 -0
- package/sdks/rust/api-full/rust/README.md +111 -0
- package/sdks/rust/api-full/rust/docs/Actor.md +24 -0
- package/sdks/rust/api-full/rust/docs/ActorName.md +11 -0
- package/sdks/rust/api-full/rust/docs/ActorsCreateApi.md +40 -0
- package/sdks/rust/api-full/rust/docs/ActorsCreateRequest.md +16 -0
- package/sdks/rust/api-full/rust/docs/ActorsCreateResponse.md +11 -0
- package/sdks/rust/api-full/rust/docs/ActorsDeleteApi.md +40 -0
- package/sdks/rust/api-full/rust/docs/ActorsGetOrCreateApi.md +40 -0
- package/sdks/rust/api-full/rust/docs/ActorsGetOrCreateRequest.md +16 -0
- package/sdks/rust/api-full/rust/docs/ActorsGetOrCreateResponse.md +12 -0
- package/sdks/rust/api-full/rust/docs/ActorsKvGetApi.md +38 -0
- package/sdks/rust/api-full/rust/docs/ActorsKvGetResponse.md +12 -0
- package/sdks/rust/api-full/rust/docs/ActorsListApi.md +45 -0
- package/sdks/rust/api-full/rust/docs/ActorsListNamesApi.md +41 -0
- package/sdks/rust/api-full/rust/docs/ActorsListNamesResponse.md +12 -0
- package/sdks/rust/api-full/rust/docs/ActorsListResponse.md +12 -0
- package/sdks/rust/api-full/rust/docs/CrashPolicy.md +14 -0
- package/sdks/rust/api-full/rust/docs/Datacenter.md +13 -0
- package/sdks/rust/api-full/rust/docs/DatacenterHealth.md +16 -0
- package/sdks/rust/api-full/rust/docs/DatacentersApi.md +34 -0
- package/sdks/rust/api-full/rust/docs/DatacentersListResponse.md +12 -0
- package/sdks/rust/api-full/rust/docs/HealthApi.md +34 -0
- package/sdks/rust/api-full/rust/docs/HealthFanoutResponse.md +11 -0
- package/sdks/rust/api-full/rust/docs/HealthResponse.md +13 -0
- package/sdks/rust/api-full/rust/docs/HealthStatus.md +13 -0
- package/sdks/rust/api-full/rust/docs/Namespace.md +14 -0
- package/sdks/rust/api-full/rust/docs/NamespaceListResponse.md +12 -0
- package/sdks/rust/api-full/rust/docs/NamespacesApi.md +69 -0
- package/sdks/rust/api-full/rust/docs/NamespacesCreateRequest.md +12 -0
- package/sdks/rust/api-full/rust/docs/NamespacesCreateResponse.md +11 -0
- package/sdks/rust/api-full/rust/docs/Pagination.md +11 -0
- package/sdks/rust/api-full/rust/docs/Runner.md +25 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfig.md +13 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigKind.md +12 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigKindOneOf.md +11 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigKindOneOf1.md +11 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigKindOneOf1Serverless.md +17 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigVariant.md +13 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsDeleteApi.md +38 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsListApi.md +41 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsListResponse.md +12 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsListResponseRunnerConfigsValue.md +11 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsRefreshMetadataApi.md +39 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessHealthCheckApi.md +38 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessHealthCheckRequest.md +12 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessHealthCheckResponse.md +12 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessHealthCheckResponseOneOf.md +11 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessHealthCheckResponseOneOf1.md +11 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessHealthCheckResponseOneOf1Failure.md +11 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessHealthCheckResponseOneOfSuccess.md +11 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessMetadataError.md +16 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessMetadataErrorOneOf.md +11 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessMetadataErrorOneOf1.md +11 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessMetadataErrorOneOf2.md +11 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessMetadataErrorOneOf3.md +11 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessMetadataErrorOneOf3NonSuccessStatus.md +12 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessMetadataErrorOneOf4.md +11 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessMetadataErrorOneOf4InvalidResponseJson.md +11 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessMetadataErrorOneOf5.md +11 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsServerlessMetadataErrorOneOf5InvalidResponseSchema.md +12 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsUpsertApi.md +39 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsUpsertRequestBody.md +11 -0
- package/sdks/rust/api-full/rust/docs/RunnerConfigsUpsertResponse.md +11 -0
- package/sdks/rust/api-full/rust/docs/RunnersApi.md +75 -0
- package/sdks/rust/api-full/rust/docs/RunnersListNamesResponse.md +12 -0
- package/sdks/rust/api-full/rust/docs/RunnersListResponse.md +12 -0
- package/sdks/rust/api-full/rust/git_push.sh +57 -0
- package/sdks/rust/api-full/rust/src/apis/actors_create_api.rs +68 -0
- package/sdks/rust/api-full/rust/src/apis/actors_delete_api.rs +69 -0
- package/sdks/rust/api-full/rust/src/apis/actors_get_or_create_api.rs +68 -0
- package/sdks/rust/api-full/rust/src/apis/actors_kv_get_api.rs +65 -0
- package/sdks/rust/api-full/rust/src/apis/actors_list_api.rs +90 -0
- package/sdks/rust/api-full/rust/src/apis/actors_list_names_api.rs +74 -0
- package/sdks/rust/api-full/rust/src/apis/configuration.rs +51 -0
- package/sdks/rust/api-full/rust/src/apis/datacenters_api.rs +62 -0
- package/sdks/rust/api-full/rust/src/apis/health_api.rs +62 -0
- package/sdks/rust/api-full/rust/src/apis/mod.rs +130 -0
- package/sdks/rust/api-full/rust/src/apis/namespaces_api.rs +126 -0
- package/sdks/rust/api-full/rust/src/apis/runner_configs_delete_api.rs +66 -0
- package/sdks/rust/api-full/rust/src/apis/runner_configs_list_api.rs +81 -0
- package/sdks/rust/api-full/rust/src/apis/runner_configs_refresh_metadata_api.rs +68 -0
- package/sdks/rust/api-full/rust/src/apis/runner_configs_serverless_health_check_api.rs +67 -0
- package/sdks/rust/api-full/rust/src/apis/runner_configs_upsert_api.rs +68 -0
- package/sdks/rust/api-full/rust/src/apis/runners_api.rs +141 -0
- package/sdks/rust/api-full/rust/src/lib.rs +11 -0
- package/sdks/rust/api-full/rust/src/models/actor.rs +73 -0
- package/sdks/rust/api-full/rust/src/models/actor_name.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/actors_create_request.rs +42 -0
- package/sdks/rust/api-full/rust/src/models/actors_create_response.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/actors_get_or_create_request.rs +42 -0
- package/sdks/rust/api-full/rust/src/models/actors_get_or_create_response.rs +30 -0
- package/sdks/rust/api-full/rust/src/models/actors_kv_get_response.rs +30 -0
- package/sdks/rust/api-full/rust/src/models/actors_list_names_response.rs +30 -0
- package/sdks/rust/api-full/rust/src/models/actors_list_response.rs +30 -0
- package/sdks/rust/api-full/rust/src/models/crash_policy.rs +41 -0
- package/sdks/rust/api-full/rust/src/models/datacenter.rs +33 -0
- package/sdks/rust/api-full/rust/src/models/datacenter_health.rs +42 -0
- package/sdks/rust/api-full/rust/src/models/datacenters_list_response.rs +30 -0
- package/sdks/rust/api-full/rust/src/models/health_fanout_response.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/health_response.rs +33 -0
- package/sdks/rust/api-full/rust/src/models/health_status.rs +38 -0
- package/sdks/rust/api-full/rust/src/models/mod.rs +100 -0
- package/sdks/rust/api-full/rust/src/models/namespace.rs +36 -0
- package/sdks/rust/api-full/rust/src/models/namespace_list_response.rs +30 -0
- package/sdks/rust/api-full/rust/src/models/namespaces_create_request.rs +30 -0
- package/sdks/rust/api-full/rust/src/models/namespaces_create_response.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/pagination.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runner.rs +69 -0
- package/sdks/rust/api-full/rust/src/models/runner_config.rs +33 -0
- package/sdks/rust/api-full/rust/src/models/runner_config_kind.rs +26 -0
- package/sdks/rust/api-full/rust/src/models/runner_config_kind_one_of.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runner_config_kind_one_of_1.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runner_config_kind_one_of_1_serverless.rs +46 -0
- package/sdks/rust/api-full/rust/src/models/runner_config_variant.rs +38 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_list_response.rs +30 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_list_response_runner_configs_value.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_request.rs +30 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response.rs +26 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of_1.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of_1_failure.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of_success.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error.rs +30 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_1.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_2.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_3.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_3_non_success_status.rs +30 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_4.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_4_invalid_response_json.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_5.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_5_invalid_response_schema.rs +30 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_upsert_request_body.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runner_configs_upsert_response.rs +27 -0
- package/sdks/rust/api-full/rust/src/models/runners_list_names_response.rs +30 -0
- package/sdks/rust/api-full/rust/src/models/runners_list_response.rs +30 -0
- package/sdks/rust/api-full/src/apis/actors_api.rs +67 -0
- package/sdks/rust/api-full/src/apis/configuration.rs +48 -0
- package/sdks/rust/api-full/src/apis/mod.rs +119 -0
- package/sdks/rust/api-full/src/apis/ns_api.rs +67 -0
- package/sdks/rust/api-full/src/lib.rs +11 -0
- package/sdks/rust/api-full/src/models/actor.rs +72 -0
- package/sdks/rust/api-full/src/models/actor_lifecycle.rs +29 -0
- package/sdks/rust/api-full/src/models/actors_create_request.rs +36 -0
- package/sdks/rust/api-full/src/models/actors_create_response.rs +26 -0
- package/sdks/rust/api-full/src/models/mod.rs +14 -0
- package/sdks/rust/api-full/src/models/namespace.rs +40 -0
- package/sdks/rust/api-full/src/models/namespaces_create_request.rs +26 -0
- package/sdks/rust/api-full/src/models/namespaces_create_response.rs +26 -0
- package/sdks/rust/data/Cargo.toml +18 -0
- package/sdks/rust/data/build.rs +15 -0
- package/sdks/rust/data/src/converted.rs +137 -0
- package/sdks/rust/data/src/generated.rs +1 -0
- package/sdks/rust/data/src/lib.rs +11 -0
- package/sdks/rust/data/src/versioned/mod.rs +178 -0
- package/sdks/rust/data/src/versioned/namespace_runner_config.rs +125 -0
- package/sdks/rust/epoxy-protocol/Cargo.toml +17 -0
- package/sdks/rust/epoxy-protocol/build.rs +18 -0
- package/sdks/rust/epoxy-protocol/src/generated.rs +1 -0
- package/sdks/rust/epoxy-protocol/src/lib.rs +7 -0
- package/sdks/rust/epoxy-protocol/src/versioned.rs +206 -0
- package/sdks/rust/runner-protocol/Cargo.toml +23 -0
- package/sdks/rust/runner-protocol/build.rs +115 -0
- package/sdks/rust/runner-protocol/src/compat.rs +7 -0
- package/sdks/rust/runner-protocol/src/generated.rs +1 -0
- package/sdks/rust/runner-protocol/src/lib.rs +10 -0
- package/sdks/rust/runner-protocol/src/util.rs +14 -0
- package/sdks/rust/runner-protocol/src/uuid_compat.rs +209 -0
- package/sdks/rust/runner-protocol/src/versioned.rs +1734 -0
- package/sdks/rust/ups-protocol/Cargo.toml +17 -0
- package/sdks/rust/ups-protocol/build.rs +18 -0
- package/sdks/rust/ups-protocol/src/generated.rs +1 -0
- package/sdks/rust/ups-protocol/src/lib.rs +7 -0
- package/sdks/rust/ups-protocol/src/versioned.rs +48 -0
- package/sdks/schemas/README.md +6 -0
- package/sdks/schemas/data/namespace.runner_config.v1.bare +13 -0
- package/sdks/schemas/data/namespace.runner_config.v2.bare +23 -0
- package/sdks/schemas/data/pegboard.namespace.actor_by_key.v1.bare +6 -0
- package/sdks/schemas/data/pegboard.namespace.actor_name.v1.bare +5 -0
- package/sdks/schemas/data/pegboard.namespace.runner_alloc_idx.v1.bare +7 -0
- package/sdks/schemas/data/pegboard.namespace.runner_by_key.v1.bare +6 -0
- package/sdks/schemas/data/pegboard.runner.metadata.v1.bare +5 -0
- package/sdks/schemas/epoxy-protocol/v1.bare +260 -0
- package/sdks/schemas/runner-protocol/v1.bare +393 -0
- package/sdks/schemas/runner-protocol/v2.bare +403 -0
- package/sdks/schemas/runner-protocol/v3.bare +436 -0
- package/sdks/schemas/ups-protocol/v1.bare +23 -0
- package/sdks/typescript/api-full/.turbo/turbo-build.log +42 -0
- package/sdks/typescript/api-full/build.js +69 -0
- package/sdks/typescript/api-full/package.json +59 -0
- package/sdks/typescript/api-full/rivetkit-engine-api-full-25.5.3.tgz +0 -0
- package/sdks/typescript/api-full/src/Client.ts +984 -0
- package/sdks/typescript/api-full/src/api/client/index.ts +1 -0
- package/sdks/typescript/api-full/src/api/client/requests/ActorsCreateRequest.ts +24 -0
- package/sdks/typescript/api-full/src/api/client/requests/ActorsDeleteRequest.ts +11 -0
- package/sdks/typescript/api-full/src/api/client/requests/ActorsGetOrCreateRequest.ts +25 -0
- package/sdks/typescript/api-full/src/api/client/requests/ActorsListNamesRequest.ts +15 -0
- package/sdks/typescript/api-full/src/api/client/requests/ActorsListRequest.ts +19 -0
- package/sdks/typescript/api-full/src/api/client/requests/RunnerConfigsDeleteRequest.ts +13 -0
- package/sdks/typescript/api-full/src/api/client/requests/RunnerConfigsListRequest.ts +19 -0
- package/sdks/typescript/api-full/src/api/client/requests/RunnerConfigsRefreshMetadataRequest.ts +19 -0
- package/sdks/typescript/api-full/src/api/client/requests/RunnerConfigsServerlessHealthCheckRequest.ts +16 -0
- package/sdks/typescript/api-full/src/api/client/requests/RunnerConfigsUpsertRequestBody.ts +19 -0
- package/sdks/typescript/api-full/src/api/client/requests/index.ts +10 -0
- package/sdks/typescript/api-full/src/api/index.ts +3 -0
- package/sdks/typescript/api-full/src/api/resources/datacenters/client/Client.ts +97 -0
- package/sdks/typescript/api-full/src/api/resources/datacenters/client/index.ts +1 -0
- package/sdks/typescript/api-full/src/api/resources/datacenters/index.ts +1 -0
- package/sdks/typescript/api-full/src/api/resources/health/client/Client.ts +97 -0
- package/sdks/typescript/api-full/src/api/resources/health/client/index.ts +1 -0
- package/sdks/typescript/api-full/src/api/resources/health/index.ts +1 -0
- package/sdks/typescript/api-full/src/api/resources/index.ts +6 -0
- package/sdks/typescript/api-full/src/api/resources/namespaces/client/Client.ts +187 -0
- package/sdks/typescript/api-full/src/api/resources/namespaces/client/index.ts +1 -0
- package/sdks/typescript/api-full/src/api/resources/namespaces/client/requests/NamespacesCreateRequest.ts +15 -0
- package/sdks/typescript/api-full/src/api/resources/namespaces/client/requests/NamespacesListRequest.ts +14 -0
- package/sdks/typescript/api-full/src/api/resources/namespaces/client/requests/index.ts +2 -0
- package/sdks/typescript/api-full/src/api/resources/namespaces/index.ts +1 -0
- package/sdks/typescript/api-full/src/api/resources/runners/client/Client.ts +209 -0
- package/sdks/typescript/api-full/src/api/resources/runners/client/index.ts +1 -0
- package/sdks/typescript/api-full/src/api/resources/runners/client/requests/RunnersListNamesRequest.ts +15 -0
- package/sdks/typescript/api-full/src/api/resources/runners/client/requests/RunnersListRequest.ts +18 -0
- package/sdks/typescript/api-full/src/api/resources/runners/client/requests/index.ts +2 -0
- package/sdks/typescript/api-full/src/api/resources/runners/index.ts +1 -0
- package/sdks/typescript/api-full/src/api/types/Actor.ts +32 -0
- package/sdks/typescript/api-full/src/api/types/ActorName.ts +7 -0
- package/sdks/typescript/api-full/src/api/types/ActorsCreateResponse.ts +9 -0
- package/sdks/typescript/api-full/src/api/types/ActorsDeleteResponse.ts +5 -0
- package/sdks/typescript/api-full/src/api/types/ActorsGetOrCreateResponse.ts +10 -0
- package/sdks/typescript/api-full/src/api/types/ActorsKvGetResponse.ts +8 -0
- package/sdks/typescript/api-full/src/api/types/ActorsListNamesResponse.ts +10 -0
- package/sdks/typescript/api-full/src/api/types/ActorsListResponse.ts +10 -0
- package/sdks/typescript/api-full/src/api/types/CrashPolicy.ts +10 -0
- package/sdks/typescript/api-full/src/api/types/Datacenter.ts +9 -0
- package/sdks/typescript/api-full/src/api/types/DatacenterHealth.ts +14 -0
- package/sdks/typescript/api-full/src/api/types/DatacentersListResponse.ts +10 -0
- package/sdks/typescript/api-full/src/api/types/HealthFanoutResponse.ts +9 -0
- package/sdks/typescript/api-full/src/api/types/HealthResponse.ts +9 -0
- package/sdks/typescript/api-full/src/api/types/HealthStatus.ts +9 -0
- package/sdks/typescript/api-full/src/api/types/Namespace.ts +12 -0
- package/sdks/typescript/api-full/src/api/types/NamespaceListResponse.ts +10 -0
- package/sdks/typescript/api-full/src/api/types/NamespacesCreateResponse.ts +9 -0
- package/sdks/typescript/api-full/src/api/types/Pagination.ts +7 -0
- package/sdks/typescript/api-full/src/api/types/RivetId.ts +5 -0
- package/sdks/typescript/api-full/src/api/types/Runner.ts +23 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfig.ts +11 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigKind.ts +7 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigKindNormal.ts +7 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigKindServerless.ts +9 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigKindServerlessServerless.ts +14 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigServerless.ts +14 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigVariant.ts +9 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsDeleteResponse.ts +5 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsListResponse.ts +10 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsListResponseRunnerConfigsValue.ts +9 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsRefreshMetadataRequestBody.ts +5 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsRefreshMetadataResponse.ts +5 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsServerlessHealthCheckResponse.ts +9 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsServerlessHealthCheckResponseFailure.ts +9 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsServerlessHealthCheckResponseFailureFailure.ts +9 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsServerlessHealthCheckResponseSuccess.ts +9 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsServerlessHealthCheckResponseSuccessSuccess.ts +7 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsServerlessMetadataError.ts +13 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsServerlessMetadataErrorInvalidRequest.ts +7 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsServerlessMetadataErrorInvalidResponseJson.ts +9 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsServerlessMetadataErrorInvalidResponseJsonInvalidResponseJson.ts +7 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsServerlessMetadataErrorInvalidResponseSchema.ts +9 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsServerlessMetadataErrorInvalidResponseSchemaInvalidResponseSchema.ts +8 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsServerlessMetadataErrorNonSuccessStatus.ts +9 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsServerlessMetadataErrorNonSuccessStatusNonSuccessStatus.ts +8 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsServerlessMetadataErrorRequestFailed.ts +7 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsServerlessMetadataErrorRequestTimedOut.ts +7 -0
- package/sdks/typescript/api-full/src/api/types/RunnerConfigsUpsertResponse.ts +7 -0
- package/sdks/typescript/api-full/src/api/types/RunnersListNamesResponse.ts +10 -0
- package/sdks/typescript/api-full/src/api/types/RunnersListResponse.ts +10 -0
- package/sdks/typescript/api-full/src/api/types/index.ts +51 -0
- package/sdks/typescript/api-full/src/core/auth/BasicAuth.ts +31 -0
- package/sdks/typescript/api-full/src/core/auth/BearerToken.ts +15 -0
- package/sdks/typescript/api-full/src/core/auth/index.ts +2 -0
- package/sdks/typescript/api-full/src/core/fetcher/APIResponse.ts +12 -0
- package/sdks/typescript/api-full/src/core/fetcher/Fetcher.ts +144 -0
- package/sdks/typescript/api-full/src/core/fetcher/Supplier.ts +11 -0
- package/sdks/typescript/api-full/src/core/fetcher/createRequestUrl.ts +10 -0
- package/sdks/typescript/api-full/src/core/fetcher/getFetchFn.ts +25 -0
- package/sdks/typescript/api-full/src/core/fetcher/getHeader.ts +8 -0
- package/sdks/typescript/api-full/src/core/fetcher/getRequestBody.ts +16 -0
- package/sdks/typescript/api-full/src/core/fetcher/getResponseBody.ts +34 -0
- package/sdks/typescript/api-full/src/core/fetcher/index.ts +5 -0
- package/sdks/typescript/api-full/src/core/fetcher/makeRequest.ts +44 -0
- package/sdks/typescript/api-full/src/core/fetcher/requestWithRetries.ts +33 -0
- package/sdks/typescript/api-full/src/core/fetcher/signals.ts +38 -0
- package/sdks/typescript/api-full/src/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts +257 -0
- package/sdks/typescript/api-full/src/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts +107 -0
- package/sdks/typescript/api-full/src/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts +243 -0
- package/sdks/typescript/api-full/src/core/fetcher/stream-wrappers/chooseStreamWrapper.ts +34 -0
- package/sdks/typescript/api-full/src/core/index.ts +4 -0
- package/sdks/typescript/api-full/src/core/json.ts +27 -0
- package/sdks/typescript/api-full/src/core/runtime/index.ts +1 -0
- package/sdks/typescript/api-full/src/core/runtime/runtime.ts +131 -0
- package/sdks/typescript/api-full/src/core/schemas/Schema.ts +101 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/bigint/bigint.ts +55 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/bigint/index.ts +1 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/date/date.ts +65 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/date/index.ts +1 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/enum/enum.ts +43 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/enum/index.ts +1 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/index.ts +14 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/lazy/index.ts +3 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/lazy/lazy.ts +32 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/lazy/lazyObject.ts +20 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/list/index.ts +1 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/list/list.ts +73 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/literals/booleanLiteral.ts +29 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/literals/index.ts +2 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/literals/stringLiteral.ts +29 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/object/index.ts +22 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/object/object.ts +366 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/object/objectWithoutOptionalProperties.ts +18 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/object/property.ts +23 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/object/types.ts +58 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/object-like/getObjectLikeUtils.ts +79 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/object-like/index.ts +2 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/object-like/types.ts +11 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/primitives/any.ts +4 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/primitives/boolean.ts +25 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/primitives/index.ts +5 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/primitives/number.ts +25 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/primitives/string.ts +25 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/primitives/unknown.ts +4 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/record/index.ts +2 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/record/record.ts +129 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/record/types.ts +17 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/schema-utils/JsonError.ts +9 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/schema-utils/ParseError.ts +9 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/schema-utils/getSchemaUtils.ts +181 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/schema-utils/index.ts +4 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/schema-utils/stringifyValidationErrors.ts +8 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/set/index.ts +1 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/set/set.ts +43 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/undiscriminated-union/index.ts +6 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/undiscriminated-union/types.ts +10 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts +60 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/union/discriminant.ts +14 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/union/index.ts +10 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/union/types.ts +26 -0
- package/sdks/typescript/api-full/src/core/schemas/builders/union/union.ts +170 -0
- package/sdks/typescript/api-full/src/core/schemas/index.ts +2 -0
- package/sdks/typescript/api-full/src/core/schemas/utils/MaybePromise.ts +1 -0
- package/sdks/typescript/api-full/src/core/schemas/utils/addQuestionMarksToNullableProperties.ts +9 -0
- package/sdks/typescript/api-full/src/core/schemas/utils/createIdentitySchemaCreator.ts +21 -0
- package/sdks/typescript/api-full/src/core/schemas/utils/entries.ts +3 -0
- package/sdks/typescript/api-full/src/core/schemas/utils/filterObject.ts +13 -0
- package/sdks/typescript/api-full/src/core/schemas/utils/getErrorMessageForIncorrectType.ts +25 -0
- package/sdks/typescript/api-full/src/core/schemas/utils/isPlainObject.ts +17 -0
- package/sdks/typescript/api-full/src/core/schemas/utils/keys.ts +3 -0
- package/sdks/typescript/api-full/src/core/schemas/utils/maybeSkipValidation.ts +38 -0
- package/sdks/typescript/api-full/src/core/schemas/utils/partition.ts +12 -0
- package/sdks/typescript/api-full/src/errors/RivetError.ts +47 -0
- package/sdks/typescript/api-full/src/errors/RivetTimeoutError.ts +10 -0
- package/sdks/typescript/api-full/src/errors/index.ts +2 -0
- package/sdks/typescript/api-full/src/index.ts +4 -0
- package/sdks/typescript/api-full/src/serialization/client/index.ts +1 -0
- package/sdks/typescript/api-full/src/serialization/client/requests/ActorsCreateRequest.ts +31 -0
- package/sdks/typescript/api-full/src/serialization/client/requests/ActorsGetOrCreateRequest.ts +31 -0
- package/sdks/typescript/api-full/src/serialization/client/requests/RunnerConfigsServerlessHealthCheckRequest.ts +22 -0
- package/sdks/typescript/api-full/src/serialization/client/requests/RunnerConfigsUpsertRequestBody.ts +21 -0
- package/sdks/typescript/api-full/src/serialization/client/requests/index.ts +4 -0
- package/sdks/typescript/api-full/src/serialization/index.ts +3 -0
- package/sdks/typescript/api-full/src/serialization/resources/index.ts +2 -0
- package/sdks/typescript/api-full/src/serialization/resources/namespaces/client/index.ts +1 -0
- package/sdks/typescript/api-full/src/serialization/resources/namespaces/client/requests/NamespacesCreateRequest.ts +22 -0
- package/sdks/typescript/api-full/src/serialization/resources/namespaces/client/requests/index.ts +1 -0
- package/sdks/typescript/api-full/src/serialization/resources/namespaces/index.ts +1 -0
- package/sdks/typescript/api-full/src/serialization/types/Actor.ts +45 -0
- package/sdks/typescript/api-full/src/serialization/types/ActorName.ts +18 -0
- package/sdks/typescript/api-full/src/serialization/types/ActorsCreateResponse.ts +21 -0
- package/sdks/typescript/api-full/src/serialization/types/ActorsDeleteResponse.ts +16 -0
- package/sdks/typescript/api-full/src/serialization/types/ActorsGetOrCreateResponse.ts +23 -0
- package/sdks/typescript/api-full/src/serialization/types/ActorsKvGetResponse.ts +22 -0
- package/sdks/typescript/api-full/src/serialization/types/ActorsListNamesResponse.ts +24 -0
- package/sdks/typescript/api-full/src/serialization/types/ActorsListResponse.ts +24 -0
- package/sdks/typescript/api-full/src/serialization/types/CrashPolicy.ts +14 -0
- package/sdks/typescript/api-full/src/serialization/types/Datacenter.ts +22 -0
- package/sdks/typescript/api-full/src/serialization/types/DatacenterHealth.ts +32 -0
- package/sdks/typescript/api-full/src/serialization/types/DatacentersListResponse.ts +24 -0
- package/sdks/typescript/api-full/src/serialization/types/HealthFanoutResponse.ts +21 -0
- package/sdks/typescript/api-full/src/serialization/types/HealthResponse.ts +22 -0
- package/sdks/typescript/api-full/src/serialization/types/HealthStatus.ts +14 -0
- package/sdks/typescript/api-full/src/serialization/types/Namespace.ts +25 -0
- package/sdks/typescript/api-full/src/serialization/types/NamespaceListResponse.ts +24 -0
- package/sdks/typescript/api-full/src/serialization/types/NamespacesCreateResponse.ts +21 -0
- package/sdks/typescript/api-full/src/serialization/types/Pagination.ts +18 -0
- package/sdks/typescript/api-full/src/serialization/types/RivetId.ts +13 -0
- package/sdks/typescript/api-full/src/serialization/types/Runner.ts +46 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfig.ts +23 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigKind.ts +16 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigKindNormal.ts +20 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigKindServerless.ts +21 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigKindServerlessServerless.ts +32 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigServerless.ts +32 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigVariant.ts +16 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsDeleteResponse.ts +16 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsListResponse.ts +27 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsListResponseRunnerConfigsValue.ts +21 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsRefreshMetadataRequestBody.ts +16 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsRefreshMetadataResponse.ts +16 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsServerlessHealthCheckResponse.ts +23 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsServerlessHealthCheckResponseFailure.ts +21 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsServerlessHealthCheckResponseFailureFailure.ts +21 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsServerlessHealthCheckResponseSuccess.ts +21 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsServerlessHealthCheckResponseSuccessSuccess.ts +20 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsServerlessMetadataError.ts +35 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsServerlessMetadataErrorInvalidRequest.ts +23 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsServerlessMetadataErrorInvalidResponseJson.ts +24 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsServerlessMetadataErrorInvalidResponseJsonInvalidResponseJson.ts +20 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsServerlessMetadataErrorInvalidResponseSchema.ts +24 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsServerlessMetadataErrorInvalidResponseSchemaInvalidResponseSchema.ts +22 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsServerlessMetadataErrorNonSuccessStatus.ts +24 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsServerlessMetadataErrorNonSuccessStatusNonSuccessStatus.ts +22 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsServerlessMetadataErrorRequestFailed.ts +23 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsServerlessMetadataErrorRequestTimedOut.ts +23 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnerConfigsUpsertResponse.ts +20 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnersListNamesResponse.ts +23 -0
- package/sdks/typescript/api-full/src/serialization/types/RunnersListResponse.ts +24 -0
- package/sdks/typescript/api-full/src/serialization/types/index.ts +51 -0
- package/sdks/typescript/api-full/tsconfig.json +24 -0
- package/sdks/typescript/runner/.turbo/turbo-build.log +22 -0
- package/sdks/typescript/runner/benches/actor-lifecycle.bench.ts +190 -0
- package/sdks/typescript/runner/benches/utils.ts +143 -0
- package/sdks/typescript/runner/dist/mod.cjs +2951 -0
- package/sdks/typescript/runner/dist/mod.cjs.map +1 -0
- package/sdks/typescript/runner/dist/mod.d.cts +326 -0
- package/sdks/typescript/runner/dist/mod.d.ts +326 -0
- package/sdks/typescript/runner/dist/mod.js +2951 -0
- package/sdks/typescript/runner/dist/mod.js.map +1 -0
- package/sdks/typescript/runner/node_modules/.bin/pino +21 -0
- package/sdks/typescript/runner/node_modules/.bin/tsc +21 -0
- package/sdks/typescript/runner/node_modules/.bin/tsserver +21 -0
- package/sdks/typescript/runner/node_modules/.bin/tsup +21 -0
- package/sdks/typescript/runner/node_modules/.bin/tsup-node +21 -0
- package/sdks/typescript/runner/node_modules/.bin/tsx +21 -0
- package/sdks/typescript/runner/node_modules/.bin/uuid +21 -0
- package/sdks/typescript/runner/node_modules/.bin/vitest +21 -0
- package/sdks/typescript/runner/package.json +37 -0
- package/sdks/typescript/runner/src/actor.ts +196 -0
- package/sdks/typescript/runner/src/log.ts +11 -0
- package/sdks/typescript/runner/src/mod.ts +1755 -0
- package/sdks/typescript/runner/src/stringify.ts +354 -0
- package/sdks/typescript/runner/src/tunnel.ts +1178 -0
- package/sdks/typescript/runner/src/utils.ts +159 -0
- package/sdks/typescript/runner/src/websocket-tunnel-adapter.ts +567 -0
- package/sdks/typescript/runner/src/websocket.ts +43 -0
- package/sdks/typescript/runner/tests/lifecycle.test.ts +596 -0
- package/sdks/typescript/runner/tests/utils.test.ts +194 -0
- package/sdks/typescript/runner/tsconfig.json +11 -0
- package/sdks/typescript/runner/tsup.config.ts +4 -0
- package/sdks/typescript/runner/turbo.json +4 -0
- package/sdks/typescript/runner/vitest.config.ts +16 -0
- package/sdks/typescript/runner-protocol/.turbo/turbo-build.log +22 -0
- package/sdks/typescript/runner-protocol/dist/index.cjs +1632 -0
- package/sdks/typescript/runner-protocol/dist/index.cjs.map +1 -0
- package/sdks/typescript/runner-protocol/dist/index.d.cts +666 -0
- package/sdks/typescript/runner-protocol/dist/index.d.ts +666 -0
- package/sdks/typescript/runner-protocol/dist/index.js +1632 -0
- package/sdks/typescript/runner-protocol/dist/index.js.map +1 -0
- package/sdks/typescript/runner-protocol/node_modules/.bin/tsc +21 -0
- package/sdks/typescript/runner-protocol/node_modules/.bin/tsserver +21 -0
- package/sdks/typescript/runner-protocol/node_modules/.bin/tsup +21 -0
- package/sdks/typescript/runner-protocol/node_modules/.bin/tsup-node +21 -0
- package/sdks/typescript/runner-protocol/package.json +34 -0
- package/sdks/typescript/runner-protocol/src/index.ts +2121 -0
- package/sdks/typescript/runner-protocol/tsconfig.json +17 -0
- package/sdks/typescript/runner-protocol/tsup.config.ts +4 -0
- package/sdks/typescript/runner-protocol/turbo.json +4 -0
- package/sdks/typescript/test-runner/.turbo/turbo-build.log +17 -0
- package/sdks/typescript/test-runner/Dockerfile +26 -0
- package/sdks/typescript/test-runner/dist/index.d.ts +6 -0
- package/sdks/typescript/test-runner/dist/index.js +345 -0
- package/sdks/typescript/test-runner/dist/index.js.map +1 -0
- package/sdks/typescript/test-runner/node_modules/.bin/pino +21 -0
- package/sdks/typescript/test-runner/node_modules/.bin/tsc +21 -0
- package/sdks/typescript/test-runner/node_modules/.bin/tsserver +21 -0
- package/sdks/typescript/test-runner/node_modules/.bin/tsup +21 -0
- package/sdks/typescript/test-runner/node_modules/.bin/tsup-node +21 -0
- package/sdks/typescript/test-runner/node_modules/.bin/tsx +21 -0
- package/sdks/typescript/test-runner/node_modules/.bin/vitest +21 -0
- package/sdks/typescript/test-runner/package.json +27 -0
- package/sdks/typescript/test-runner/src/index.ts +287 -0
- package/sdks/typescript/test-runner/src/log.ts +194 -0
- package/sdks/typescript/test-runner/tsconfig.json +11 -0
- package/sdks/typescript/test-runner/tsup.config.ts +7 -0
- package/sdks/typescript/test-runner/turbo.json +4 -0
- package/sdks/typescript/test-runner/vitest.config.ts +16 -0
- package/tests/load/README.md +28 -0
- package/tests/load/actor-lifecycle/README.md +26 -0
- package/tests/load/actor-lifecycle/actor.ts +41 -0
- package/tests/load/actor-lifecycle/config.ts +14 -0
- package/tests/load/actor-lifecycle/index.ts +62 -0
- package/tests/load/actor-lifecycle/rivet_api.ts +140 -0
- package/tests/load/actor-lifecycle/types.ts +17 -0
- package/tests/load/node_modules/.bin/biome +21 -0
- package/tests/load/node_modules/.bin/tsc +21 -0
- package/tests/load/node_modules/.bin/tsserver +21 -0
- package/tests/load/package.json +15 -0
- package/tests/load/tsconfig.json +20 -0
- package/tests/smoke/README.md +32 -0
- package/tests/smoke/package.json +19 -0
- package/tests/smoke/scripts/connect.ts +41 -0
- package/tests/smoke/src/server/registry.ts +32 -0
- package/tests/smoke/src/server/server.ts +7 -0
- package/tests/smoke/src/smoke-test/index.ts +161 -0
- package/tests/smoke/src/smoke-test/spawn-actor.ts +109 -0
- package/tests/smoke/tsconfig.json +43 -0
- package/tests/smoke/turbo.json +4 -0
|
@@ -0,0 +1,2699 @@
|
|
|
1
|
+
use anyhow::{Context, Result, bail, ensure};
|
|
2
|
+
use bytes::Bytes;
|
|
3
|
+
use futures_util::{SinkExt, StreamExt};
|
|
4
|
+
use http_body_util::{BodyExt, Full};
|
|
5
|
+
use hyper::{Request, Response, StatusCode, body::Incoming as BodyIncoming, header::HeaderName};
|
|
6
|
+
use hyper_tungstenite;
|
|
7
|
+
use hyper_util::{client::legacy::Client, rt::TokioExecutor};
|
|
8
|
+
use moka::future::Cache;
|
|
9
|
+
use rand;
|
|
10
|
+
use rivet_api_builder::{ErrorResponse, RawErrorResponse};
|
|
11
|
+
use rivet_api_builder::{RequestIds, X_RIVET_RAY_ID};
|
|
12
|
+
use rivet_error::{INTERNAL_ERROR, RivetError};
|
|
13
|
+
use rivet_metrics::KeyValue;
|
|
14
|
+
use rivet_util::Id;
|
|
15
|
+
use serde_json;
|
|
16
|
+
|
|
17
|
+
use rivet_runner_protocol as protocol;
|
|
18
|
+
use std::{
|
|
19
|
+
borrow::Cow,
|
|
20
|
+
collections::{HashMap as StdHashMap, HashSet},
|
|
21
|
+
net::SocketAddr,
|
|
22
|
+
sync::Arc,
|
|
23
|
+
time::{Duration, Instant},
|
|
24
|
+
};
|
|
25
|
+
use tokio::sync::Mutex;
|
|
26
|
+
use tokio::time::timeout;
|
|
27
|
+
use tokio_tungstenite::tungstenite::{
|
|
28
|
+
client::IntoClientRequest,
|
|
29
|
+
protocol::{CloseFrame, frame::coding::CloseCode},
|
|
30
|
+
};
|
|
31
|
+
use tracing::Instrument;
|
|
32
|
+
use url::Url;
|
|
33
|
+
|
|
34
|
+
use crate::{
|
|
35
|
+
WebSocketHandle,
|
|
36
|
+
custom_serve::{CustomServeTrait, HibernationResult},
|
|
37
|
+
errors, metrics,
|
|
38
|
+
request_context::RequestContext,
|
|
39
|
+
task_group::TaskGroup,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const X_RIVET_TARGET: HeaderName = HeaderName::from_static("x-rivet-target");
|
|
43
|
+
const X_RIVET_ACTOR: HeaderName = HeaderName::from_static("x-rivet-actor");
|
|
44
|
+
const X_RIVET_TOKEN: HeaderName = HeaderName::from_static("x-rivet-token");
|
|
45
|
+
pub const X_FORWARDED_FOR: HeaderName = HeaderName::from_static("x-forwarded-for");
|
|
46
|
+
pub const X_RIVET_ERROR: HeaderName = HeaderName::from_static("x-rivet-error");
|
|
47
|
+
|
|
48
|
+
const ROUTE_CACHE_TTL: Duration = Duration::from_secs(60 * 10); // 10 minutes
|
|
49
|
+
const PROXY_STATE_CACHE_TTL: Duration = Duration::from_secs(60 * 60); // 1 hour
|
|
50
|
+
const WEBSOCKET_CLOSE_LINGER: Duration = Duration::from_millis(100); // Keep TCP connection open briefly after WebSocket close
|
|
51
|
+
|
|
52
|
+
/// Response body type that can handle both streaming and buffered responses
|
|
53
|
+
#[derive(Debug)]
|
|
54
|
+
pub enum ResponseBody {
|
|
55
|
+
/// Buffered response body
|
|
56
|
+
Full(Full<Bytes>),
|
|
57
|
+
/// Streaming response body
|
|
58
|
+
Incoming(BodyIncoming),
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
impl http_body::Body for ResponseBody {
|
|
62
|
+
type Data = Bytes;
|
|
63
|
+
type Error = Box<dyn std::error::Error + Send + Sync>;
|
|
64
|
+
|
|
65
|
+
fn poll_frame(
|
|
66
|
+
self: std::pin::Pin<&mut Self>,
|
|
67
|
+
cx: &mut std::task::Context<'_>,
|
|
68
|
+
) -> std::task::Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
|
|
69
|
+
match self.get_mut() {
|
|
70
|
+
ResponseBody::Full(body) => {
|
|
71
|
+
let pin = std::pin::Pin::new(body);
|
|
72
|
+
match pin.poll_frame(cx) {
|
|
73
|
+
std::task::Poll::Ready(Some(Ok(frame))) => {
|
|
74
|
+
std::task::Poll::Ready(Some(Ok(frame)))
|
|
75
|
+
}
|
|
76
|
+
std::task::Poll::Ready(Some(Err(e))) => {
|
|
77
|
+
std::task::Poll::Ready(Some(Err(Box::new(e))))
|
|
78
|
+
}
|
|
79
|
+
std::task::Poll::Ready(None) => std::task::Poll::Ready(None),
|
|
80
|
+
std::task::Poll::Pending => std::task::Poll::Pending,
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
ResponseBody::Incoming(body) => {
|
|
84
|
+
let pin = std::pin::Pin::new(body);
|
|
85
|
+
match pin.poll_frame(cx) {
|
|
86
|
+
std::task::Poll::Ready(Some(Ok(frame))) => {
|
|
87
|
+
std::task::Poll::Ready(Some(Ok(frame)))
|
|
88
|
+
}
|
|
89
|
+
std::task::Poll::Ready(Some(Err(e))) => {
|
|
90
|
+
std::task::Poll::Ready(Some(Err(Box::new(e))))
|
|
91
|
+
}
|
|
92
|
+
std::task::Poll::Ready(None) => std::task::Poll::Ready(None),
|
|
93
|
+
std::task::Poll::Pending => std::task::Poll::Pending,
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
fn is_end_stream(&self) -> bool {
|
|
100
|
+
match self {
|
|
101
|
+
ResponseBody::Full(body) => body.is_end_stream(),
|
|
102
|
+
ResponseBody::Incoming(body) => body.is_end_stream(),
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
fn size_hint(&self) -> http_body::SizeHint {
|
|
107
|
+
match self {
|
|
108
|
+
ResponseBody::Full(body) => body.size_hint(),
|
|
109
|
+
ResponseBody::Incoming(body) => body.size_hint(),
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Routing types
|
|
115
|
+
#[derive(Clone, Debug)]
|
|
116
|
+
pub struct RouteTarget {
|
|
117
|
+
pub actor_id: Option<Id>,
|
|
118
|
+
pub host: String,
|
|
119
|
+
pub port: u16,
|
|
120
|
+
pub path: String,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
#[derive(Clone, Debug)]
|
|
124
|
+
pub struct RoutingTimeout {
|
|
125
|
+
pub routing_timeout: u64, // in seconds
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
#[derive(Clone, Debug)]
|
|
129
|
+
pub struct RouteConfig {
|
|
130
|
+
pub targets: Vec<RouteTarget>,
|
|
131
|
+
pub timeout: RoutingTimeout,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
#[derive(Clone)]
|
|
135
|
+
pub enum RoutingOutput {
|
|
136
|
+
/// Return the data to route to.
|
|
137
|
+
Route(RouteConfig),
|
|
138
|
+
/// Return a custom response.
|
|
139
|
+
Response(StructuredResponse),
|
|
140
|
+
/// Return a custom serve handler.
|
|
141
|
+
CustomServe(Arc<dyn CustomServeTrait>),
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#[derive(Clone, Debug)]
|
|
145
|
+
pub struct StructuredResponse {
|
|
146
|
+
pub status: StatusCode,
|
|
147
|
+
pub message: Cow<'static, str>,
|
|
148
|
+
pub docs: Option<Cow<'static, str>>,
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
impl StructuredResponse {
|
|
152
|
+
pub fn build_response(&self) -> Result<Response<ResponseBody>> {
|
|
153
|
+
let mut body = StdHashMap::new();
|
|
154
|
+
body.insert("message", self.message.clone().into_owned());
|
|
155
|
+
|
|
156
|
+
if let Some(docs) = &self.docs {
|
|
157
|
+
body.insert("docs", docs.clone().into_owned());
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let body_json = serde_json::to_string(&body)?;
|
|
161
|
+
let bytes = Bytes::from(body_json);
|
|
162
|
+
|
|
163
|
+
let response = Response::builder()
|
|
164
|
+
.status(self.status)
|
|
165
|
+
.header(hyper::header::CONTENT_TYPE, "application/json")
|
|
166
|
+
.body(ResponseBody::Full(Full::new(bytes)))?;
|
|
167
|
+
|
|
168
|
+
Ok(response)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
#[derive(Clone)]
|
|
173
|
+
enum ResolveRouteOutput {
|
|
174
|
+
Target(RouteTarget),
|
|
175
|
+
Response(StructuredResponse),
|
|
176
|
+
CustomServe(Arc<dyn CustomServeTrait>),
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/// Enum defining the type of port the request came in on
|
|
180
|
+
#[derive(Clone, Debug, PartialEq)]
|
|
181
|
+
pub enum PortType {
|
|
182
|
+
Http,
|
|
183
|
+
Https,
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
pub type RoutingFn = Arc<
|
|
187
|
+
dyn for<'a> Fn(
|
|
188
|
+
&'a str,
|
|
189
|
+
&'a str,
|
|
190
|
+
PortType,
|
|
191
|
+
&'a hyper::HeaderMap,
|
|
192
|
+
) -> futures::future::BoxFuture<'a, Result<RoutingOutput>>
|
|
193
|
+
+ Send
|
|
194
|
+
+ Sync,
|
|
195
|
+
>;
|
|
196
|
+
|
|
197
|
+
pub type CacheKeyFn = Arc<
|
|
198
|
+
dyn for<'a> Fn(
|
|
199
|
+
&'a str,
|
|
200
|
+
&'a str,
|
|
201
|
+
&'a hyper::Method,
|
|
202
|
+
PortType,
|
|
203
|
+
&'a hyper::HeaderMap,
|
|
204
|
+
) -> Result<u64>
|
|
205
|
+
+ Send
|
|
206
|
+
+ Sync,
|
|
207
|
+
>;
|
|
208
|
+
|
|
209
|
+
#[derive(Clone, Debug)]
|
|
210
|
+
pub struct MiddlewareConfig {
|
|
211
|
+
pub rate_limit: RateLimitConfig,
|
|
212
|
+
pub max_in_flight: MaxInFlightConfig,
|
|
213
|
+
pub retry: RetryConfig,
|
|
214
|
+
pub timeout: TimeoutConfig,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
#[derive(Clone, Debug)]
|
|
218
|
+
pub struct RateLimitConfig {
|
|
219
|
+
pub requests: u64,
|
|
220
|
+
pub period: u64, // in seconds
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
#[derive(Clone, Debug)]
|
|
224
|
+
pub struct MaxInFlightConfig {
|
|
225
|
+
pub amount: usize,
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
#[derive(Clone, Debug)]
|
|
229
|
+
pub struct RetryConfig {
|
|
230
|
+
pub max_attempts: u32,
|
|
231
|
+
pub initial_interval: u64, // in milliseconds
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
#[derive(Clone, Debug)]
|
|
235
|
+
pub struct TimeoutConfig {
|
|
236
|
+
pub request_timeout: u64, // in seconds
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
#[derive(Clone, Debug)]
|
|
240
|
+
pub enum MiddlewareResponse {
|
|
241
|
+
Ok(MiddlewareConfig),
|
|
242
|
+
NotFound,
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
pub type MiddlewareFn = Arc<
|
|
246
|
+
dyn for<'a> Fn(
|
|
247
|
+
&'a Id,
|
|
248
|
+
&'a hyper::HeaderMap,
|
|
249
|
+
) -> futures::future::BoxFuture<'a, Result<MiddlewareResponse>>
|
|
250
|
+
+ Send
|
|
251
|
+
+ Sync,
|
|
252
|
+
>;
|
|
253
|
+
|
|
254
|
+
// Cache for routing results
|
|
255
|
+
struct RouteCache {
|
|
256
|
+
cache: Cache<u64, RouteConfig>,
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
impl RouteCache {
|
|
260
|
+
fn new() -> Self {
|
|
261
|
+
Self {
|
|
262
|
+
cache: Cache::builder()
|
|
263
|
+
.max_capacity(10_000)
|
|
264
|
+
.time_to_live(ROUTE_CACHE_TTL)
|
|
265
|
+
.build(),
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
#[tracing::instrument(skip_all)]
|
|
270
|
+
async fn get(&self, key: &u64) -> Option<RouteConfig> {
|
|
271
|
+
self.cache.get(key).await
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
#[tracing::instrument(skip_all)]
|
|
275
|
+
async fn insert(&self, key: u64, result: RouteConfig) {
|
|
276
|
+
self.cache.insert(key, result).await;
|
|
277
|
+
|
|
278
|
+
metrics::ROUTE_CACHE_COUNT.record(self.cache.entry_count(), &[]);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Rate limiter
|
|
283
|
+
struct RateLimiter {
|
|
284
|
+
requests_remaining: u64,
|
|
285
|
+
reset_time: Instant,
|
|
286
|
+
requests_limit: u64,
|
|
287
|
+
period: Duration,
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
impl RateLimiter {
|
|
291
|
+
fn new(requests: u64, period_seconds: u64) -> Self {
|
|
292
|
+
Self {
|
|
293
|
+
requests_remaining: requests,
|
|
294
|
+
reset_time: Instant::now() + Duration::from_secs(period_seconds),
|
|
295
|
+
requests_limit: requests,
|
|
296
|
+
period: Duration::from_secs(period_seconds),
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
fn try_acquire(&mut self) -> bool {
|
|
301
|
+
let now = Instant::now();
|
|
302
|
+
|
|
303
|
+
// Check if we need to reset the counter
|
|
304
|
+
if now >= self.reset_time {
|
|
305
|
+
self.requests_remaining = self.requests_limit;
|
|
306
|
+
self.reset_time = now + self.period;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Try to consume a request
|
|
310
|
+
if self.requests_remaining > 0 {
|
|
311
|
+
self.requests_remaining -= 1;
|
|
312
|
+
true
|
|
313
|
+
} else {
|
|
314
|
+
false
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// In-flight requests counter
|
|
320
|
+
struct InFlightCounter {
|
|
321
|
+
count: usize,
|
|
322
|
+
max: usize,
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
impl InFlightCounter {
|
|
326
|
+
fn new(max: usize) -> Self {
|
|
327
|
+
Self { count: 0, max }
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
fn try_acquire(&mut self) -> bool {
|
|
331
|
+
if self.count < self.max {
|
|
332
|
+
self.count += 1;
|
|
333
|
+
true
|
|
334
|
+
} else {
|
|
335
|
+
false
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
fn release(&mut self) {
|
|
340
|
+
self.count = self.count.saturating_sub(1);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// State shared across all request handlers
|
|
345
|
+
pub struct ProxyState {
|
|
346
|
+
config: rivet_config::Config,
|
|
347
|
+
routing_fn: RoutingFn,
|
|
348
|
+
cache_key_fn: CacheKeyFn,
|
|
349
|
+
middleware_fn: MiddlewareFn,
|
|
350
|
+
route_cache: RouteCache,
|
|
351
|
+
rate_limiters: Cache<(Id, std::net::IpAddr), Arc<Mutex<RateLimiter>>>,
|
|
352
|
+
in_flight_counters: Cache<(Id, std::net::IpAddr), Arc<Mutex<InFlightCounter>>>,
|
|
353
|
+
in_flight_requests: Arc<Mutex<HashSet<protocol::RequestId>>>,
|
|
354
|
+
port_type: PortType,
|
|
355
|
+
clickhouse_inserter: Option<clickhouse_inserter::ClickHouseInserterHandle>,
|
|
356
|
+
tasks: Arc<TaskGroup>,
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
impl ProxyState {
|
|
360
|
+
pub fn new(
|
|
361
|
+
config: rivet_config::Config,
|
|
362
|
+
routing_fn: RoutingFn,
|
|
363
|
+
cache_key_fn: CacheKeyFn,
|
|
364
|
+
middleware_fn: MiddlewareFn,
|
|
365
|
+
port_type: PortType,
|
|
366
|
+
clickhouse_inserter: Option<clickhouse_inserter::ClickHouseInserterHandle>,
|
|
367
|
+
) -> Self {
|
|
368
|
+
Self {
|
|
369
|
+
config,
|
|
370
|
+
routing_fn,
|
|
371
|
+
cache_key_fn,
|
|
372
|
+
middleware_fn,
|
|
373
|
+
route_cache: RouteCache::new(),
|
|
374
|
+
rate_limiters: Cache::builder()
|
|
375
|
+
.max_capacity(10_000)
|
|
376
|
+
.time_to_live(PROXY_STATE_CACHE_TTL)
|
|
377
|
+
.build(),
|
|
378
|
+
in_flight_counters: Cache::builder()
|
|
379
|
+
.max_capacity(10_000)
|
|
380
|
+
.time_to_live(PROXY_STATE_CACHE_TTL)
|
|
381
|
+
.build(),
|
|
382
|
+
in_flight_requests: Arc::new(Mutex::new(HashSet::new())),
|
|
383
|
+
port_type,
|
|
384
|
+
clickhouse_inserter,
|
|
385
|
+
tasks: TaskGroup::new(),
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
#[tracing::instrument(skip(self))]
|
|
390
|
+
async fn resolve_route(
|
|
391
|
+
&self,
|
|
392
|
+
hostname: &str,
|
|
393
|
+
path: &str,
|
|
394
|
+
method: &hyper::Method,
|
|
395
|
+
port_type: PortType,
|
|
396
|
+
headers: &hyper::HeaderMap,
|
|
397
|
+
ignore_cache: bool,
|
|
398
|
+
) -> Result<ResolveRouteOutput> {
|
|
399
|
+
// Extract just the hostname, stripping the port if present
|
|
400
|
+
let hostname_only = hostname.split(':').next().unwrap_or(hostname);
|
|
401
|
+
|
|
402
|
+
tracing::debug!(
|
|
403
|
+
hostname = %hostname_only,
|
|
404
|
+
path = %path,
|
|
405
|
+
method = %method,
|
|
406
|
+
port_type = ?port_type,
|
|
407
|
+
"Resolving route for request"
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
let cache_key =
|
|
411
|
+
(self.cache_key_fn)(hostname_only, &path, method, port_type.clone(), headers)?;
|
|
412
|
+
|
|
413
|
+
// Check cache first
|
|
414
|
+
if !ignore_cache {
|
|
415
|
+
if let Some(result) = self.route_cache.get(&cache_key).await {
|
|
416
|
+
// Choose a random target from the cached targets
|
|
417
|
+
if let Some(target) = choose_random_target(&result.targets) {
|
|
418
|
+
return Ok(ResolveRouteOutput::Target(target.clone()));
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Not in cache, call routing function with a default timeout
|
|
424
|
+
// Default 15 seconds, routing functions should have their own internal timeouts that are shorter
|
|
425
|
+
let default_timeout = Duration::from_secs(15);
|
|
426
|
+
|
|
427
|
+
tracing::debug!(
|
|
428
|
+
hostname = %hostname_only,
|
|
429
|
+
path = %path,
|
|
430
|
+
cache_hit = false,
|
|
431
|
+
timeout_seconds = default_timeout.as_secs(),
|
|
432
|
+
"Cache miss, calling routing function"
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
let res = timeout(
|
|
436
|
+
default_timeout,
|
|
437
|
+
(self.routing_fn)(hostname_only, path, port_type, headers),
|
|
438
|
+
)
|
|
439
|
+
.await
|
|
440
|
+
.map_err(|_| {
|
|
441
|
+
errors::RequestTimeout {
|
|
442
|
+
timeout_seconds: default_timeout.as_secs(),
|
|
443
|
+
}
|
|
444
|
+
.build()
|
|
445
|
+
})?;
|
|
446
|
+
|
|
447
|
+
match res? {
|
|
448
|
+
RoutingOutput::Route(result) => {
|
|
449
|
+
tracing::debug!(
|
|
450
|
+
hostname = %hostname_only,
|
|
451
|
+
path = %path,
|
|
452
|
+
targets_count = result.targets.len(),
|
|
453
|
+
timeout_secs = result.timeout.routing_timeout,
|
|
454
|
+
"Received routing result"
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
// Cache the result
|
|
458
|
+
self.route_cache.insert(cache_key, result.clone()).await;
|
|
459
|
+
tracing::debug!("Added route to cache");
|
|
460
|
+
|
|
461
|
+
// Choose a random target
|
|
462
|
+
if let Some(target) = choose_random_target(&result.targets) {
|
|
463
|
+
tracing::debug!(
|
|
464
|
+
hostname = %hostname_only,
|
|
465
|
+
path = %path,
|
|
466
|
+
target_host = %target.host,
|
|
467
|
+
target_port = target.port,
|
|
468
|
+
target_path = %target.path,
|
|
469
|
+
actor_id = ?target.actor_id,
|
|
470
|
+
"Selected target for request"
|
|
471
|
+
);
|
|
472
|
+
Ok(ResolveRouteOutput::Target(target.clone()))
|
|
473
|
+
} else {
|
|
474
|
+
tracing::warn!(
|
|
475
|
+
hostname = %hostname_only,
|
|
476
|
+
path = %path,
|
|
477
|
+
"No route targets available from result"
|
|
478
|
+
);
|
|
479
|
+
Err(errors::NoRouteTargets.build())
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
RoutingOutput::Response(response) => {
|
|
483
|
+
tracing::debug!(
|
|
484
|
+
hostname = %hostname_only,
|
|
485
|
+
path = %path,
|
|
486
|
+
status = ?response.status,
|
|
487
|
+
"Routing returned custom response"
|
|
488
|
+
);
|
|
489
|
+
Ok(ResolveRouteOutput::Response(response))
|
|
490
|
+
}
|
|
491
|
+
RoutingOutput::CustomServe(handler) => {
|
|
492
|
+
tracing::debug!(
|
|
493
|
+
hostname = %hostname_only,
|
|
494
|
+
path = %path,
|
|
495
|
+
"Routing returned custom serve handler"
|
|
496
|
+
);
|
|
497
|
+
Ok(ResolveRouteOutput::CustomServe(handler))
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
#[tracing::instrument(skip_all)]
|
|
503
|
+
async fn get_middleware_config(
|
|
504
|
+
&self,
|
|
505
|
+
actor_id: &Id,
|
|
506
|
+
headers: &hyper::HeaderMap,
|
|
507
|
+
) -> Result<MiddlewareConfig> {
|
|
508
|
+
// Call the middleware function with a timeout
|
|
509
|
+
let default_timeout = Duration::from_secs(5); // Default 5 seconds
|
|
510
|
+
|
|
511
|
+
let middleware_result =
|
|
512
|
+
timeout(default_timeout, (self.middleware_fn)(actor_id, headers)).await;
|
|
513
|
+
|
|
514
|
+
match middleware_result {
|
|
515
|
+
Ok(result) => match result? {
|
|
516
|
+
MiddlewareResponse::Ok(config) => Ok(config),
|
|
517
|
+
MiddlewareResponse::NotFound => {
|
|
518
|
+
// Default values if middleware not found for this actor
|
|
519
|
+
Ok(MiddlewareConfig {
|
|
520
|
+
rate_limit: RateLimitConfig {
|
|
521
|
+
requests: 100, // 100 requests
|
|
522
|
+
period: 60, // per 60 seconds
|
|
523
|
+
},
|
|
524
|
+
max_in_flight: MaxInFlightConfig {
|
|
525
|
+
amount: 20, // 20 concurrent requests
|
|
526
|
+
},
|
|
527
|
+
retry: RetryConfig {
|
|
528
|
+
max_attempts: 3, // 3 retry attempts
|
|
529
|
+
initial_interval: 100, // 100ms initial interval
|
|
530
|
+
},
|
|
531
|
+
timeout: TimeoutConfig {
|
|
532
|
+
request_timeout: 30, // 30 seconds for requests
|
|
533
|
+
},
|
|
534
|
+
})
|
|
535
|
+
}
|
|
536
|
+
},
|
|
537
|
+
Err(_) => {
|
|
538
|
+
// Default values if middleware times out
|
|
539
|
+
Ok(MiddlewareConfig {
|
|
540
|
+
rate_limit: RateLimitConfig {
|
|
541
|
+
requests: 100, // 100 requests
|
|
542
|
+
period: 60, // per 60 seconds
|
|
543
|
+
},
|
|
544
|
+
max_in_flight: MaxInFlightConfig {
|
|
545
|
+
amount: 20, // 20 concurrent requests
|
|
546
|
+
},
|
|
547
|
+
retry: RetryConfig {
|
|
548
|
+
max_attempts: 3, // 3 retry attempts
|
|
549
|
+
initial_interval: 100, // 100ms initial interval
|
|
550
|
+
},
|
|
551
|
+
timeout: TimeoutConfig {
|
|
552
|
+
request_timeout: 30, // 30 seconds for requests
|
|
553
|
+
},
|
|
554
|
+
})
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
#[tracing::instrument(skip_all)]
|
|
560
|
+
async fn check_rate_limit(
|
|
561
|
+
&self,
|
|
562
|
+
ip_addr: std::net::IpAddr,
|
|
563
|
+
actor_id: &Option<Id>,
|
|
564
|
+
headers: &hyper::HeaderMap,
|
|
565
|
+
) -> Result<bool> {
|
|
566
|
+
let Some(actor_id) = *actor_id else {
|
|
567
|
+
// No rate limiting when actor_id is None
|
|
568
|
+
return Ok(true);
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
// Get actor-specific middleware config
|
|
572
|
+
let middleware_config = self.get_middleware_config(&actor_id, headers).await?;
|
|
573
|
+
|
|
574
|
+
let cache_key = (actor_id, ip_addr);
|
|
575
|
+
|
|
576
|
+
// Get existing limiter or create a new one
|
|
577
|
+
let limiter_arc = if let Some(existing_limiter) = self.rate_limiters.get(&cache_key).await {
|
|
578
|
+
existing_limiter
|
|
579
|
+
} else {
|
|
580
|
+
let new_limiter = Arc::new(Mutex::new(RateLimiter::new(
|
|
581
|
+
middleware_config.rate_limit.requests,
|
|
582
|
+
middleware_config.rate_limit.period,
|
|
583
|
+
)));
|
|
584
|
+
self.rate_limiters
|
|
585
|
+
.insert(cache_key, new_limiter.clone())
|
|
586
|
+
.await;
|
|
587
|
+
metrics::RATE_LIMITER_COUNT.record(self.rate_limiters.entry_count(), &[]);
|
|
588
|
+
new_limiter
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
// Try to acquire from the limiter
|
|
592
|
+
let result = {
|
|
593
|
+
let mut limiter = limiter_arc.lock().await;
|
|
594
|
+
limiter.try_acquire()
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
Ok(result)
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
#[tracing::instrument(skip_all)]
|
|
601
|
+
async fn acquire_in_flight(
|
|
602
|
+
&self,
|
|
603
|
+
ip_addr: std::net::IpAddr,
|
|
604
|
+
actor_id: &Option<Id>,
|
|
605
|
+
headers: &hyper::HeaderMap,
|
|
606
|
+
) -> Result<Option<protocol::RequestId>> {
|
|
607
|
+
// Check in-flight limit if actor_id is present
|
|
608
|
+
if let Some(actor_id) = *actor_id {
|
|
609
|
+
// Get actor-specific middleware config
|
|
610
|
+
let middleware_config = self.get_middleware_config(&actor_id, headers).await?;
|
|
611
|
+
|
|
612
|
+
let cache_key = (actor_id, ip_addr);
|
|
613
|
+
|
|
614
|
+
// Get existing counter or create a new one
|
|
615
|
+
let counter_arc = if let Some(existing_counter) =
|
|
616
|
+
self.in_flight_counters.get(&cache_key).await
|
|
617
|
+
{
|
|
618
|
+
existing_counter
|
|
619
|
+
} else {
|
|
620
|
+
let new_counter = Arc::new(Mutex::new(InFlightCounter::new(
|
|
621
|
+
middleware_config.max_in_flight.amount,
|
|
622
|
+
)));
|
|
623
|
+
self.in_flight_counters
|
|
624
|
+
.insert(cache_key, new_counter.clone())
|
|
625
|
+
.await;
|
|
626
|
+
metrics::IN_FLIGHT_COUNTER_COUNT.record(self.in_flight_counters.entry_count(), &[]);
|
|
627
|
+
new_counter
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
// Try to acquire from the counter
|
|
631
|
+
let acquired = {
|
|
632
|
+
let mut counter = counter_arc.lock().await;
|
|
633
|
+
counter.try_acquire()
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
if !acquired {
|
|
637
|
+
return Ok(None); // Rate limited
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// Generate unique request ID
|
|
642
|
+
let request_id = Some(self.generate_unique_request_id().await?);
|
|
643
|
+
Ok(request_id)
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
#[tracing::instrument(skip_all)]
|
|
647
|
+
async fn release_in_flight(
|
|
648
|
+
&self,
|
|
649
|
+
ip_addr: std::net::IpAddr,
|
|
650
|
+
actor_id: &Option<Id>,
|
|
651
|
+
request_id: protocol::RequestId,
|
|
652
|
+
) {
|
|
653
|
+
// Release in-flight counter if actor_id is present
|
|
654
|
+
if let Some(actor_id) = *actor_id {
|
|
655
|
+
let cache_key = (actor_id, ip_addr);
|
|
656
|
+
if let Some(counter_arc) = self.in_flight_counters.get(&cache_key).await {
|
|
657
|
+
let mut counter = counter_arc.lock().await;
|
|
658
|
+
counter.release();
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// Release request ID
|
|
663
|
+
let mut requests = self.in_flight_requests.lock().await;
|
|
664
|
+
requests.remove(&request_id);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/// Generate a unique request ID that is not currently in flight
|
|
668
|
+
async fn generate_unique_request_id(&self) -> Result<protocol::RequestId> {
|
|
669
|
+
const MAX_TRIES: u32 = 100;
|
|
670
|
+
let mut requests = self.in_flight_requests.lock().await;
|
|
671
|
+
|
|
672
|
+
for attempt in 0..MAX_TRIES {
|
|
673
|
+
let request_id = protocol::util::generate_request_id();
|
|
674
|
+
|
|
675
|
+
// Check if this ID is already in use
|
|
676
|
+
if !requests.contains(&request_id) {
|
|
677
|
+
// Insert the ID and return it
|
|
678
|
+
requests.insert(request_id);
|
|
679
|
+
return Ok(request_id);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// Collision occurred (extremely rare with 4 bytes = 4 billion possibilities)
|
|
683
|
+
// Generate a new ID and try again
|
|
684
|
+
tracing::warn!(
|
|
685
|
+
?request_id,
|
|
686
|
+
attempt,
|
|
687
|
+
"request id collision, generating new id"
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
bail!(
|
|
692
|
+
"failed to generate unique request id after {} attempts",
|
|
693
|
+
MAX_TRIES
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// Helper function to choose a random target from a list of targets
|
|
699
|
+
fn choose_random_target(targets: &[RouteTarget]) -> Option<&RouteTarget> {
|
|
700
|
+
if targets.is_empty() {
|
|
701
|
+
return None;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// Use a simple random index selection
|
|
705
|
+
let random_index = rand::random::<usize>() % targets.len();
|
|
706
|
+
targets.get(random_index)
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Proxy service
|
|
710
|
+
pub struct ProxyService {
|
|
711
|
+
state: Arc<ProxyState>,
|
|
712
|
+
remote_addr: SocketAddr,
|
|
713
|
+
// Note: Using the hyper legacy client is the only option currently.
|
|
714
|
+
// This is what reqwest uses under the hood. Eventually we'll migrate to h3 once it's ready.
|
|
715
|
+
client: Client<hyper_util::client::legacy::connect::HttpConnector, Full<Bytes>>,
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
impl ProxyService {
|
|
719
|
+
pub fn new(state: Arc<ProxyState>, remote_addr: SocketAddr) -> Self {
|
|
720
|
+
// Create a client with the hyper-util legacy client
|
|
721
|
+
let client = Client::builder(TokioExecutor::new())
|
|
722
|
+
.pool_idle_timeout(Duration::from_secs(30))
|
|
723
|
+
.build_http();
|
|
724
|
+
|
|
725
|
+
Self {
|
|
726
|
+
state,
|
|
727
|
+
remote_addr,
|
|
728
|
+
client,
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// Calculate backoff duration for a given retry attempt
|
|
733
|
+
pub fn calculate_backoff(attempt: u32, initial_interval: u64) -> Duration {
|
|
734
|
+
Duration::from_millis(initial_interval * 2u64.pow(attempt - 1))
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
#[tracing::instrument(skip_all)]
|
|
738
|
+
async fn handle_request(
|
|
739
|
+
&self,
|
|
740
|
+
req: Request<BodyIncoming>,
|
|
741
|
+
start_time: Instant,
|
|
742
|
+
request_context: &mut RequestContext,
|
|
743
|
+
) -> Result<Response<ResponseBody>> {
|
|
744
|
+
let host = req
|
|
745
|
+
.headers()
|
|
746
|
+
.get(hyper::header::HOST)
|
|
747
|
+
.and_then(|h| h.to_str().ok())
|
|
748
|
+
.unwrap_or("unknown");
|
|
749
|
+
|
|
750
|
+
let path = req
|
|
751
|
+
.uri()
|
|
752
|
+
.path_and_query()
|
|
753
|
+
.map(|x| x.to_string())
|
|
754
|
+
.unwrap_or_else(|| req.uri().path().to_string());
|
|
755
|
+
|
|
756
|
+
// Set request body size in analytics (will be updated with actual size later)
|
|
757
|
+
|
|
758
|
+
let target_res = self
|
|
759
|
+
.state
|
|
760
|
+
.resolve_route(
|
|
761
|
+
host,
|
|
762
|
+
&path,
|
|
763
|
+
req.method(),
|
|
764
|
+
self.state.port_type.clone(),
|
|
765
|
+
req.headers(),
|
|
766
|
+
false,
|
|
767
|
+
)
|
|
768
|
+
.await;
|
|
769
|
+
|
|
770
|
+
let duration_secs = start_time.elapsed().as_secs_f64();
|
|
771
|
+
metrics::RESOLVE_ROUTE_DURATION.record(duration_secs, &[]);
|
|
772
|
+
|
|
773
|
+
// Resolve target
|
|
774
|
+
let target = target_res?;
|
|
775
|
+
if let ResolveRouteOutput::Response(response) = &target {
|
|
776
|
+
// Return the custom response
|
|
777
|
+
return response.build_response();
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
let actor_id = if let ResolveRouteOutput::Target(target) = &target {
|
|
781
|
+
target.actor_id
|
|
782
|
+
} else {
|
|
783
|
+
None
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
// Extract IP address from X-Forwarded-For header or fall back to remote_addr
|
|
787
|
+
let client_ip = req
|
|
788
|
+
.headers()
|
|
789
|
+
.get(X_FORWARDED_FOR)
|
|
790
|
+
.and_then(|h| h.to_str().ok())
|
|
791
|
+
.and_then(|forwarded| {
|
|
792
|
+
// X-Forwarded-For can be a comma-separated list, take the first IP
|
|
793
|
+
forwarded.split(',').next().map(|s| s.trim())
|
|
794
|
+
})
|
|
795
|
+
.and_then(|ip_str| ip_str.parse::<std::net::IpAddr>().ok())
|
|
796
|
+
.unwrap_or_else(|| self.remote_addr.ip());
|
|
797
|
+
|
|
798
|
+
// Apply rate limiting
|
|
799
|
+
if !self
|
|
800
|
+
.state
|
|
801
|
+
.check_rate_limit(client_ip, &actor_id, req.headers())
|
|
802
|
+
.await?
|
|
803
|
+
{
|
|
804
|
+
return Err(errors::RateLimit {
|
|
805
|
+
actor_id,
|
|
806
|
+
method: req.method().to_string(),
|
|
807
|
+
path: path.clone(),
|
|
808
|
+
ip: client_ip.to_string(),
|
|
809
|
+
}
|
|
810
|
+
.build());
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// Acquire in-flight limit and generate request ID
|
|
814
|
+
let request_id = match self
|
|
815
|
+
.state
|
|
816
|
+
.acquire_in_flight(client_ip, &actor_id, req.headers())
|
|
817
|
+
.await?
|
|
818
|
+
{
|
|
819
|
+
Some(id) => id,
|
|
820
|
+
None => {
|
|
821
|
+
return Err(errors::RateLimit {
|
|
822
|
+
actor_id,
|
|
823
|
+
method: req.method().to_string(),
|
|
824
|
+
path: path.clone(),
|
|
825
|
+
ip: client_ip.to_string(),
|
|
826
|
+
}
|
|
827
|
+
.build());
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
// Increment metrics
|
|
832
|
+
metrics::PROXY_REQUEST_PENDING.add(1, &[]);
|
|
833
|
+
metrics::PROXY_REQUEST_TOTAL.add(1, &[]);
|
|
834
|
+
|
|
835
|
+
// Update request context with target info
|
|
836
|
+
if let Some(actor_id) = actor_id {
|
|
837
|
+
request_context.service_actor_id = Some(actor_id);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
let res = if hyper_tungstenite::is_upgrade_request(&req) {
|
|
841
|
+
self.handle_websocket_upgrade(req, target, request_context, client_ip, actor_id)
|
|
842
|
+
.await
|
|
843
|
+
} else {
|
|
844
|
+
self.handle_http_request(req, target, request_context, client_ip, actor_id)
|
|
845
|
+
.await
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
let status = match &res {
|
|
849
|
+
Ok(resp) => resp.status().as_u16().to_string(),
|
|
850
|
+
Err(_) => "error".to_string(),
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
// Record metrics
|
|
854
|
+
let duration_secs = start_time.elapsed().as_secs_f64();
|
|
855
|
+
metrics::PROXY_REQUEST_DURATION
|
|
856
|
+
.record(duration_secs, &[KeyValue::new("status", status.clone())]);
|
|
857
|
+
|
|
858
|
+
metrics::PROXY_REQUEST_PENDING.add(-1, &[]);
|
|
859
|
+
|
|
860
|
+
// Release in-flight counter and request ID when done
|
|
861
|
+
let state_clone = self.state.clone();
|
|
862
|
+
tokio::spawn(
|
|
863
|
+
async move {
|
|
864
|
+
state_clone
|
|
865
|
+
.release_in_flight(client_ip, &actor_id, request_id)
|
|
866
|
+
.await;
|
|
867
|
+
}
|
|
868
|
+
.instrument(tracing::info_span!("release_in_flight_task")),
|
|
869
|
+
);
|
|
870
|
+
|
|
871
|
+
res
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
#[tracing::instrument(skip_all)]
|
|
875
|
+
async fn handle_http_request(
|
|
876
|
+
&self,
|
|
877
|
+
req: Request<BodyIncoming>,
|
|
878
|
+
resolved_route: ResolveRouteOutput,
|
|
879
|
+
request_context: &mut RequestContext,
|
|
880
|
+
client_ip: std::net::IpAddr,
|
|
881
|
+
actor_id: Option<Id>,
|
|
882
|
+
) -> Result<Response<ResponseBody>> {
|
|
883
|
+
// Get middleware config for this actor if it exists
|
|
884
|
+
let middleware_config = if let ResolveRouteOutput::Target(target) = &resolved_route
|
|
885
|
+
&& let Some(actor_id) = &target.actor_id
|
|
886
|
+
{
|
|
887
|
+
self.state
|
|
888
|
+
.get_middleware_config(actor_id, req.headers())
|
|
889
|
+
.await?
|
|
890
|
+
} else {
|
|
891
|
+
// Default middleware config for targets without actor_id
|
|
892
|
+
MiddlewareConfig {
|
|
893
|
+
rate_limit: RateLimitConfig {
|
|
894
|
+
requests: 100, // 100 requests
|
|
895
|
+
period: 60, // per 60 seconds
|
|
896
|
+
},
|
|
897
|
+
max_in_flight: MaxInFlightConfig {
|
|
898
|
+
amount: 20, // 20 concurrent requests
|
|
899
|
+
},
|
|
900
|
+
retry: RetryConfig {
|
|
901
|
+
max_attempts: 3, // 3 retry attempts
|
|
902
|
+
initial_interval: 100, // 100ms initial interval
|
|
903
|
+
},
|
|
904
|
+
timeout: TimeoutConfig {
|
|
905
|
+
request_timeout: 30, // 30 seconds for requests
|
|
906
|
+
},
|
|
907
|
+
}
|
|
908
|
+
};
|
|
909
|
+
|
|
910
|
+
let host = req
|
|
911
|
+
.headers()
|
|
912
|
+
.get(hyper::header::HOST)
|
|
913
|
+
.and_then(|h| h.to_str().ok())
|
|
914
|
+
.unwrap_or("unknown")
|
|
915
|
+
.to_string();
|
|
916
|
+
|
|
917
|
+
let path = req
|
|
918
|
+
.uri()
|
|
919
|
+
.path_and_query()
|
|
920
|
+
.map(|x| x.to_string())
|
|
921
|
+
.unwrap_or_else(|| req.uri().path().to_string());
|
|
922
|
+
|
|
923
|
+
// Set up retry with backoff from middleware config
|
|
924
|
+
let max_attempts = middleware_config.retry.max_attempts;
|
|
925
|
+
let initial_interval = middleware_config.retry.initial_interval;
|
|
926
|
+
let timeout_duration = Duration::from_secs(middleware_config.timeout.request_timeout);
|
|
927
|
+
|
|
928
|
+
match resolved_route {
|
|
929
|
+
ResolveRouteOutput::Target(mut target) => {
|
|
930
|
+
// Set service IP from target
|
|
931
|
+
if let Ok(target_ip) =
|
|
932
|
+
format!("{}:{}", target.host, target.port).parse::<std::net::SocketAddr>()
|
|
933
|
+
{
|
|
934
|
+
request_context.service_ip = Some(target_ip.ip());
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// Read the request body before proceeding with retries
|
|
938
|
+
let (req_parts, body) = req.into_parts();
|
|
939
|
+
let req_body = match http_body_util::BodyExt::collect(body).await {
|
|
940
|
+
Ok(collected) => collected.to_bytes(),
|
|
941
|
+
Err(err) => {
|
|
942
|
+
tracing::debug!(?err, "Failed to read request body");
|
|
943
|
+
Bytes::new()
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
|
|
947
|
+
// Set actual request body size in analytics
|
|
948
|
+
request_context.client_request_body_bytes = Some(req_body.len() as u64);
|
|
949
|
+
|
|
950
|
+
// Use a value-returning loop to handle both errors and successful responses
|
|
951
|
+
let mut attempts = 0;
|
|
952
|
+
while attempts < max_attempts {
|
|
953
|
+
attempts += 1;
|
|
954
|
+
|
|
955
|
+
// Use the common function to build request parts
|
|
956
|
+
let builder = self
|
|
957
|
+
.proxied_request_builder(&req_parts, &target)
|
|
958
|
+
.map_err(|err| errors::HttpRequestBuildFailed(err.to_string()).build())?;
|
|
959
|
+
|
|
960
|
+
// Create the final request with body
|
|
961
|
+
let proxied_req = builder
|
|
962
|
+
.body(Full::<Bytes>::new(req_body.clone()))
|
|
963
|
+
.map_err(|err| errors::RequestBuildError(err.to_string()).build())?;
|
|
964
|
+
|
|
965
|
+
// Send the request with timeout
|
|
966
|
+
let res = timeout(timeout_duration, self.client.request(proxied_req))
|
|
967
|
+
.await
|
|
968
|
+
.map_err(|_| {
|
|
969
|
+
errors::RequestTimeout {
|
|
970
|
+
timeout_seconds: timeout_duration.as_secs(),
|
|
971
|
+
}
|
|
972
|
+
.build()
|
|
973
|
+
})?;
|
|
974
|
+
|
|
975
|
+
match res {
|
|
976
|
+
Ok(resp) => {
|
|
977
|
+
// Check if this is a retryable response
|
|
978
|
+
if should_retry_request_inner(resp.status(), resp.headers()) {
|
|
979
|
+
// Request connect error, might retry
|
|
980
|
+
tracing::debug!(
|
|
981
|
+
"Request attempt {attempts} failed (service unavailable)"
|
|
982
|
+
);
|
|
983
|
+
|
|
984
|
+
// Use backoff and continue
|
|
985
|
+
let backoff = Self::calculate_backoff(attempts, initial_interval);
|
|
986
|
+
tokio::time::sleep(backoff).await;
|
|
987
|
+
|
|
988
|
+
// Resolve target again, this time ignoring cache. This makes sure
|
|
989
|
+
// we always re-fetch the route on error
|
|
990
|
+
let ResolveRouteOutput::Target(new_target) = self
|
|
991
|
+
.state
|
|
992
|
+
.resolve_route(
|
|
993
|
+
&host,
|
|
994
|
+
&path,
|
|
995
|
+
&req_parts.method,
|
|
996
|
+
self.state.port_type.clone(),
|
|
997
|
+
&req_parts.headers,
|
|
998
|
+
true,
|
|
999
|
+
)
|
|
1000
|
+
.await?
|
|
1001
|
+
else {
|
|
1002
|
+
bail!("resolved route does not match Target");
|
|
1003
|
+
};
|
|
1004
|
+
target = new_target;
|
|
1005
|
+
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
let (parts, body) = resp.into_parts();
|
|
1010
|
+
|
|
1011
|
+
// Check if this is a streaming response by examining headers
|
|
1012
|
+
// let is_streaming = parts.headers.get("content-type")
|
|
1013
|
+
// .and_then(|ct| ct.to_str().ok())
|
|
1014
|
+
// .map(|ct| ct.contains("text/event-stream") || ct.contains("application/stream"))
|
|
1015
|
+
// .unwrap_or(false);
|
|
1016
|
+
let is_streaming = true;
|
|
1017
|
+
|
|
1018
|
+
if is_streaming {
|
|
1019
|
+
// For streaming responses, pass through the body without buffering
|
|
1020
|
+
tracing::debug!("Detected streaming response, preserving stream");
|
|
1021
|
+
|
|
1022
|
+
// We can't easily calculate response size for streaming, so set it to None
|
|
1023
|
+
request_context.guard_response_body_bytes = None;
|
|
1024
|
+
|
|
1025
|
+
let streaming_body = ResponseBody::Incoming(body);
|
|
1026
|
+
return Ok(Response::from_parts(parts, streaming_body));
|
|
1027
|
+
} else {
|
|
1028
|
+
// For non-streaming responses, buffer as before
|
|
1029
|
+
let body_bytes = match BodyExt::collect(body).await {
|
|
1030
|
+
Ok(collected) => collected.to_bytes(),
|
|
1031
|
+
Err(_) => Bytes::new(),
|
|
1032
|
+
};
|
|
1033
|
+
|
|
1034
|
+
// Set actual response body size in analytics
|
|
1035
|
+
request_context.guard_response_body_bytes =
|
|
1036
|
+
Some(body_bytes.len() as u64);
|
|
1037
|
+
|
|
1038
|
+
let full_body = ResponseBody::Full(Full::new(body_bytes));
|
|
1039
|
+
return Ok(Response::from_parts(parts, full_body));
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
Err(err) => {
|
|
1043
|
+
if !err.is_connect() || attempts >= max_attempts {
|
|
1044
|
+
tracing::error!(
|
|
1045
|
+
?err,
|
|
1046
|
+
?target,
|
|
1047
|
+
"Request error after {} attempts",
|
|
1048
|
+
attempts
|
|
1049
|
+
);
|
|
1050
|
+
|
|
1051
|
+
return Err(errors::UpstreamError(format!(
|
|
1052
|
+
"Failed to connect to runner: {err}. Make sure your runners are healthy."
|
|
1053
|
+
))
|
|
1054
|
+
.build());
|
|
1055
|
+
} else {
|
|
1056
|
+
// Request connect error, might retry
|
|
1057
|
+
tracing::debug!(?err, "Request attempt {attempts} failed");
|
|
1058
|
+
|
|
1059
|
+
// Use backoff and continue
|
|
1060
|
+
let backoff = Self::calculate_backoff(attempts, initial_interval);
|
|
1061
|
+
tokio::time::sleep(backoff).await;
|
|
1062
|
+
|
|
1063
|
+
// Resolve target again, this time ignoring cache. This makes sure
|
|
1064
|
+
// we always re-fetch the route on error
|
|
1065
|
+
let ResolveRouteOutput::Target(new_target) = self
|
|
1066
|
+
.state
|
|
1067
|
+
.resolve_route(
|
|
1068
|
+
&host,
|
|
1069
|
+
&path,
|
|
1070
|
+
&req_parts.method,
|
|
1071
|
+
self.state.port_type.clone(),
|
|
1072
|
+
&req_parts.headers,
|
|
1073
|
+
true,
|
|
1074
|
+
)
|
|
1075
|
+
.await?
|
|
1076
|
+
else {
|
|
1077
|
+
bail!("resolved route does not match Target");
|
|
1078
|
+
};
|
|
1079
|
+
target = new_target;
|
|
1080
|
+
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// If we get here, all attempts failed
|
|
1088
|
+
return Err(errors::RetryAttemptsExceeded {
|
|
1089
|
+
attempts: max_attempts,
|
|
1090
|
+
}
|
|
1091
|
+
.build());
|
|
1092
|
+
}
|
|
1093
|
+
ResolveRouteOutput::Response(_) => {
|
|
1094
|
+
unreachable!()
|
|
1095
|
+
}
|
|
1096
|
+
ResolveRouteOutput::CustomServe(mut handler) => {
|
|
1097
|
+
let req_headers = req.headers().clone();
|
|
1098
|
+
let req_method = req.method().clone();
|
|
1099
|
+
|
|
1100
|
+
// Acquire in-flight limit and generate request ID
|
|
1101
|
+
let request_id = match self
|
|
1102
|
+
.state
|
|
1103
|
+
.acquire_in_flight(client_ip, &actor_id, &req_headers)
|
|
1104
|
+
.await?
|
|
1105
|
+
{
|
|
1106
|
+
Some(id) => id,
|
|
1107
|
+
None => {
|
|
1108
|
+
return Err(errors::RateLimit {
|
|
1109
|
+
actor_id,
|
|
1110
|
+
method: req_method.to_string(),
|
|
1111
|
+
path: path.clone(),
|
|
1112
|
+
ip: client_ip.to_string(),
|
|
1113
|
+
}
|
|
1114
|
+
.build());
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
|
|
1118
|
+
// Collect request body
|
|
1119
|
+
let (req_parts, body) = req.into_parts();
|
|
1120
|
+
let collected_body = match http_body_util::BodyExt::collect(body).await {
|
|
1121
|
+
Ok(collected) => collected.to_bytes(),
|
|
1122
|
+
Err(err) => {
|
|
1123
|
+
tracing::debug!(?err, "Failed to read request body");
|
|
1124
|
+
Bytes::new()
|
|
1125
|
+
}
|
|
1126
|
+
};
|
|
1127
|
+
let req_collected = hyper::Request::from_parts(
|
|
1128
|
+
req_parts,
|
|
1129
|
+
Full::<Bytes>::new(collected_body.clone()),
|
|
1130
|
+
);
|
|
1131
|
+
|
|
1132
|
+
// Attempt request
|
|
1133
|
+
let mut attempts = 0;
|
|
1134
|
+
while attempts < max_attempts {
|
|
1135
|
+
attempts += 1;
|
|
1136
|
+
|
|
1137
|
+
let res = handler
|
|
1138
|
+
.handle_request(req_collected.clone(), request_context, request_id)
|
|
1139
|
+
.await;
|
|
1140
|
+
if should_retry_request(&res) {
|
|
1141
|
+
// Request connect error, might retry
|
|
1142
|
+
tracing::debug!("Request attempt {attempts} failed (service unavailable)");
|
|
1143
|
+
|
|
1144
|
+
// Use backoff and continue
|
|
1145
|
+
let backoff = Self::calculate_backoff(attempts, initial_interval);
|
|
1146
|
+
tokio::time::sleep(backoff).await;
|
|
1147
|
+
|
|
1148
|
+
// Refresh route (ignore cache) so subsequent requests can hit new target
|
|
1149
|
+
let ResolveRouteOutput::CustomServe(new_handler) = self
|
|
1150
|
+
.state
|
|
1151
|
+
.resolve_route(
|
|
1152
|
+
&host,
|
|
1153
|
+
&path,
|
|
1154
|
+
req_collected.method(),
|
|
1155
|
+
self.state.port_type.clone(),
|
|
1156
|
+
&req_headers,
|
|
1157
|
+
true,
|
|
1158
|
+
)
|
|
1159
|
+
.await?
|
|
1160
|
+
else {
|
|
1161
|
+
bail!("resolved route does not match CustomServe");
|
|
1162
|
+
};
|
|
1163
|
+
handler = new_handler;
|
|
1164
|
+
|
|
1165
|
+
continue;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
// Release in-flight counter and request ID before returning
|
|
1169
|
+
self.state
|
|
1170
|
+
.release_in_flight(client_ip, &actor_id, request_id)
|
|
1171
|
+
.await;
|
|
1172
|
+
return res;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// If we get here, all attempts failed
|
|
1176
|
+
// Release in-flight counter and request ID before returning error
|
|
1177
|
+
self.state
|
|
1178
|
+
.release_in_flight(client_ip, &actor_id, request_id)
|
|
1179
|
+
.await;
|
|
1180
|
+
return Err(errors::RetryAttemptsExceeded {
|
|
1181
|
+
attempts: max_attempts,
|
|
1182
|
+
}
|
|
1183
|
+
.build());
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
/// Modifies the incoming request before it is proxied.
|
|
1189
|
+
fn proxied_request_builder(
|
|
1190
|
+
&self,
|
|
1191
|
+
req_parts: &hyper::http::request::Parts,
|
|
1192
|
+
target: &RouteTarget,
|
|
1193
|
+
) -> Result<hyper::http::request::Builder> {
|
|
1194
|
+
let scheme = if target.port == 443 { "https" } else { "http" };
|
|
1195
|
+
|
|
1196
|
+
// Bracket raw IPv6 hosts
|
|
1197
|
+
let host = if target.host.contains(':') && !target.host.starts_with('[') {
|
|
1198
|
+
format!("[{}]", target.host)
|
|
1199
|
+
} else {
|
|
1200
|
+
target.host.clone()
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
// Ensure path starts with a leading slash
|
|
1204
|
+
let path = if target.path.starts_with('/') {
|
|
1205
|
+
target.path.clone()
|
|
1206
|
+
} else {
|
|
1207
|
+
format!("/{}", target.path)
|
|
1208
|
+
};
|
|
1209
|
+
|
|
1210
|
+
let url = Url::parse(&format!("{scheme}://{host}:{}{}", target.port, path))
|
|
1211
|
+
.context("invalid scheme/host/port when building URL")?;
|
|
1212
|
+
|
|
1213
|
+
// Build the proxied request
|
|
1214
|
+
let mut builder = hyper::Request::builder()
|
|
1215
|
+
.method(req_parts.method.clone())
|
|
1216
|
+
.uri(url.to_string());
|
|
1217
|
+
|
|
1218
|
+
// Modify proxy headers
|
|
1219
|
+
let headers = builder
|
|
1220
|
+
.headers_mut()
|
|
1221
|
+
.expect("request builder unexpectedly in error state");
|
|
1222
|
+
|
|
1223
|
+
headers.remove(X_RIVET_TARGET);
|
|
1224
|
+
headers.remove(X_RIVET_ACTOR);
|
|
1225
|
+
headers.remove(X_RIVET_TOKEN);
|
|
1226
|
+
|
|
1227
|
+
add_proxy_headers_with_addr(headers, &req_parts.headers, self.remote_addr)?;
|
|
1228
|
+
|
|
1229
|
+
Ok(builder)
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
#[tracing::instrument(skip_all)]
|
|
1233
|
+
async fn handle_websocket_upgrade(
|
|
1234
|
+
&self,
|
|
1235
|
+
req: Request<BodyIncoming>,
|
|
1236
|
+
target: ResolveRouteOutput,
|
|
1237
|
+
request_context: &mut RequestContext,
|
|
1238
|
+
client_ip: std::net::IpAddr,
|
|
1239
|
+
actor_id: Option<Id>,
|
|
1240
|
+
) -> Result<Response<ResponseBody>> {
|
|
1241
|
+
// Parsed for retries later
|
|
1242
|
+
let req_host = req
|
|
1243
|
+
.headers()
|
|
1244
|
+
.get(hyper::header::HOST)
|
|
1245
|
+
.and_then(|h| h.to_str().ok())
|
|
1246
|
+
.unwrap_or("unknown")
|
|
1247
|
+
.to_string();
|
|
1248
|
+
let req_path = req
|
|
1249
|
+
.uri()
|
|
1250
|
+
.path_and_query()
|
|
1251
|
+
.map(|x| x.to_string())
|
|
1252
|
+
.unwrap_or_else(|| req.uri().path().to_string());
|
|
1253
|
+
|
|
1254
|
+
// Capture headers and method before request is consumed
|
|
1255
|
+
let req_headers = req.headers().clone();
|
|
1256
|
+
let req_method = req.method().clone();
|
|
1257
|
+
let ray_id = req.extensions().get::<RequestIds>().map(|x| x.ray_id);
|
|
1258
|
+
|
|
1259
|
+
// Get middleware config for this actor if it exists
|
|
1260
|
+
let middleware_config = match &actor_id {
|
|
1261
|
+
Some(actor_id) => {
|
|
1262
|
+
self.state
|
|
1263
|
+
.get_middleware_config(actor_id, &req_headers)
|
|
1264
|
+
.await?
|
|
1265
|
+
}
|
|
1266
|
+
None => {
|
|
1267
|
+
// Default middleware config for targets without actor_id
|
|
1268
|
+
tracing::debug!("Using default middleware config (no actor_id)");
|
|
1269
|
+
MiddlewareConfig {
|
|
1270
|
+
rate_limit: RateLimitConfig {
|
|
1271
|
+
requests: 100, // 100 requests
|
|
1272
|
+
period: 60, // per 60 seconds
|
|
1273
|
+
},
|
|
1274
|
+
max_in_flight: MaxInFlightConfig {
|
|
1275
|
+
amount: 20, // 20 concurrent requests
|
|
1276
|
+
},
|
|
1277
|
+
retry: RetryConfig {
|
|
1278
|
+
max_attempts: 3, // 3 retry attempts
|
|
1279
|
+
initial_interval: 100, // 100ms initial interval
|
|
1280
|
+
},
|
|
1281
|
+
timeout: TimeoutConfig {
|
|
1282
|
+
request_timeout: 30, // 30 seconds for requests
|
|
1283
|
+
},
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
|
|
1288
|
+
// Set up retry with backoff from middleware config
|
|
1289
|
+
let max_attempts = middleware_config.retry.max_attempts;
|
|
1290
|
+
let initial_interval = middleware_config.retry.initial_interval;
|
|
1291
|
+
|
|
1292
|
+
// Log the headers for debugging
|
|
1293
|
+
tracing::debug!("WebSocket upgrade request headers:");
|
|
1294
|
+
for (name, value) in req.headers() {
|
|
1295
|
+
if let Ok(val) = value.to_str() {
|
|
1296
|
+
tracing::debug!(" {}: {}", name, val);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
// Handle WebSocket upgrade properly with hyper_tungstenite
|
|
1301
|
+
tracing::debug!(%req_path, "Upgrading client connection to WebSocket");
|
|
1302
|
+
let (client_response, client_ws) = match hyper_tungstenite::upgrade(req, None) {
|
|
1303
|
+
Ok(x) => {
|
|
1304
|
+
tracing::debug!("Client WebSocket upgrade successful");
|
|
1305
|
+
x
|
|
1306
|
+
}
|
|
1307
|
+
Err(err) => {
|
|
1308
|
+
tracing::error!(?err, "Failed to upgrade client WebSocket");
|
|
1309
|
+
return Err(errors::ConnectionError {
|
|
1310
|
+
error_message: format!("Failed to upgrade client WebSocket: {}", err),
|
|
1311
|
+
remote_addr: self.remote_addr.to_string(),
|
|
1312
|
+
}
|
|
1313
|
+
.build());
|
|
1314
|
+
}
|
|
1315
|
+
};
|
|
1316
|
+
|
|
1317
|
+
// Log response status and headers
|
|
1318
|
+
tracing::debug!(
|
|
1319
|
+
"Client upgrade response status: {}",
|
|
1320
|
+
client_response.status()
|
|
1321
|
+
);
|
|
1322
|
+
for (name, value) in client_response.headers() {
|
|
1323
|
+
if let Ok(val) = value.to_str() {
|
|
1324
|
+
tracing::debug!("Client upgrade response header - {}: {}", name, val);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
// Clone needed values for the spawned task
|
|
1329
|
+
let state = self.state.clone();
|
|
1330
|
+
let remote_addr = self.remote_addr;
|
|
1331
|
+
|
|
1332
|
+
// Spawn a new task to handle the WebSocket bidirectional communication
|
|
1333
|
+
match target {
|
|
1334
|
+
ResolveRouteOutput::Target(mut target) => {
|
|
1335
|
+
tracing::debug!("Spawning task to handle WebSocket communication");
|
|
1336
|
+
self.state.tasks.spawn(
|
|
1337
|
+
async move {
|
|
1338
|
+
// Set up a timeout for the entire operation
|
|
1339
|
+
let timeout_duration = Duration::from_secs(30); // 30 seconds timeout
|
|
1340
|
+
tracing::debug!(
|
|
1341
|
+
"WebSocket proxy task started with {}s timeout",
|
|
1342
|
+
timeout_duration.as_secs()
|
|
1343
|
+
);
|
|
1344
|
+
|
|
1345
|
+
// Use retry logic to connect to the upstream WebSocket server
|
|
1346
|
+
let mut attempts = 0;
|
|
1347
|
+
let mut upstream_ws = None;
|
|
1348
|
+
|
|
1349
|
+
// First, wait for the client WebSocket to be ready (do this first to avoid race conditions)
|
|
1350
|
+
tracing::debug!("Waiting for client WebSocket to be ready...");
|
|
1351
|
+
let mut client_ws =
|
|
1352
|
+
match tokio::time::timeout(timeout_duration, client_ws).await {
|
|
1353
|
+
Ok(Ok(ws)) => {
|
|
1354
|
+
tracing::debug!("Client WebSocket is ready");
|
|
1355
|
+
ws
|
|
1356
|
+
}
|
|
1357
|
+
Ok(Err(err)) => {
|
|
1358
|
+
tracing::error!(?err, "Failed to get client WebSocket");
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1361
|
+
Err(_) => {
|
|
1362
|
+
tracing::error!(
|
|
1363
|
+
"Timeout waiting for client WebSocket to be ready after {}s",
|
|
1364
|
+
timeout_duration.as_secs()
|
|
1365
|
+
);
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
};
|
|
1369
|
+
|
|
1370
|
+
// Now attempt to connect to the upstream server
|
|
1371
|
+
tracing::debug!("Attempting connect to upstream WebSocket");
|
|
1372
|
+
while attempts < max_attempts {
|
|
1373
|
+
attempts += 1;
|
|
1374
|
+
|
|
1375
|
+
// Build the WebSocket URL using the url crate to properly handle IPv6 addresses
|
|
1376
|
+
let mut ws_url = match Url::parse("ws://example.com") {
|
|
1377
|
+
Ok(url) => url,
|
|
1378
|
+
Err(err) => {
|
|
1379
|
+
tracing::error!(?err, "Failed to create base WebSocket URL");
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
|
|
1384
|
+
// Wrap IPv6 addresses in brackets if not already wrapped
|
|
1385
|
+
let host = if target.host.contains(':') && !target.host.starts_with('[')
|
|
1386
|
+
{
|
|
1387
|
+
format!("[{}]", target.host)
|
|
1388
|
+
} else {
|
|
1389
|
+
target.host.clone()
|
|
1390
|
+
};
|
|
1391
|
+
|
|
1392
|
+
if let Err(err) = ws_url.set_host(Some(&host)) {
|
|
1393
|
+
tracing::error!(?err, ?host, "Failed to set WebSocket host");
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
if let Err(err) = ws_url.set_port(Some(target.port)) {
|
|
1397
|
+
tracing::error!(?err, "Failed to set WebSocket port");
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
// Split path and query string
|
|
1402
|
+
if let Some(query_pos) = target.path.find('?') {
|
|
1403
|
+
let (path, query) = target.path.split_at(query_pos);
|
|
1404
|
+
ws_url.set_path(path);
|
|
1405
|
+
// Remove the leading '?' from query
|
|
1406
|
+
ws_url.set_query(Some(&query[1..]));
|
|
1407
|
+
} else {
|
|
1408
|
+
ws_url.set_path(&target.path);
|
|
1409
|
+
ws_url.set_query(None);
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
let target_url = ws_url.to_string();
|
|
1413
|
+
|
|
1414
|
+
tracing::debug!(
|
|
1415
|
+
"WebSocket request attempt {}/{} to {}",
|
|
1416
|
+
attempts,
|
|
1417
|
+
max_attempts,
|
|
1418
|
+
target_url
|
|
1419
|
+
);
|
|
1420
|
+
|
|
1421
|
+
// Build the websocket request with headers
|
|
1422
|
+
let mut ws_request = match target_url.into_client_request() {
|
|
1423
|
+
Ok(req) => req,
|
|
1424
|
+
Err(err) => {
|
|
1425
|
+
tracing::error!(?err, "Failed to create websocket request");
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
};
|
|
1429
|
+
|
|
1430
|
+
// Add proxy headers to the websocket request
|
|
1431
|
+
if let Err(err) = add_proxy_headers_with_addr(
|
|
1432
|
+
ws_request.headers_mut(),
|
|
1433
|
+
&req_headers,
|
|
1434
|
+
remote_addr,
|
|
1435
|
+
) {
|
|
1436
|
+
tracing::error!(
|
|
1437
|
+
?err,
|
|
1438
|
+
"Failed to add proxy headers to websocket request"
|
|
1439
|
+
);
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
match tokio::time::timeout(
|
|
1444
|
+
Duration::from_secs(5), // 5 second timeout per connection attempt
|
|
1445
|
+
tokio_tungstenite::connect_async(ws_request),
|
|
1446
|
+
)
|
|
1447
|
+
.await
|
|
1448
|
+
{
|
|
1449
|
+
Ok(Ok((ws_stream, resp))) => {
|
|
1450
|
+
tracing::debug!(
|
|
1451
|
+
"Successfully connected to upstream WebSocket server"
|
|
1452
|
+
);
|
|
1453
|
+
tracing::debug!(
|
|
1454
|
+
"Upstream connection response status: {:?}",
|
|
1455
|
+
resp.status()
|
|
1456
|
+
);
|
|
1457
|
+
|
|
1458
|
+
// Log headers for debugging
|
|
1459
|
+
for (name, value) in resp.headers() {
|
|
1460
|
+
if let Ok(val) = value.to_str() {
|
|
1461
|
+
tracing::debug!(
|
|
1462
|
+
"Upstream response header - {}: {}",
|
|
1463
|
+
name,
|
|
1464
|
+
val
|
|
1465
|
+
);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
upstream_ws = Some(ws_stream);
|
|
1470
|
+
break;
|
|
1471
|
+
}
|
|
1472
|
+
Ok(Err(err)) => {
|
|
1473
|
+
tracing::debug!(
|
|
1474
|
+
?err,
|
|
1475
|
+
"WebSocket request attempt {} failed",
|
|
1476
|
+
attempts
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
1479
|
+
Err(_) => {
|
|
1480
|
+
tracing::debug!(
|
|
1481
|
+
"WebSocket request attempt {} timed out after 5s",
|
|
1482
|
+
attempts
|
|
1483
|
+
);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
// Check if we've reached max attempts
|
|
1488
|
+
if attempts >= max_attempts {
|
|
1489
|
+
tracing::debug!(
|
|
1490
|
+
"All {} WebSocket connection attempts failed",
|
|
1491
|
+
max_attempts
|
|
1492
|
+
);
|
|
1493
|
+
|
|
1494
|
+
// Send a close message to the client since we can't connect to upstream
|
|
1495
|
+
tracing::debug!(
|
|
1496
|
+
"Sending close message to client due to upstream connection failure"
|
|
1497
|
+
);
|
|
1498
|
+
let (mut client_sink, _) = client_ws.split();
|
|
1499
|
+
match client_sink
|
|
1500
|
+
.send(to_hyper_close(Some(err_to_close_frame(
|
|
1501
|
+
errors::RetryAttemptsExceeded { attempts }.build(),
|
|
1502
|
+
ray_id,
|
|
1503
|
+
))))
|
|
1504
|
+
.await
|
|
1505
|
+
{
|
|
1506
|
+
Ok(_) => {
|
|
1507
|
+
tracing::trace!("Successfully sent close message to client")
|
|
1508
|
+
}
|
|
1509
|
+
Err(err) => {
|
|
1510
|
+
tracing::error!(
|
|
1511
|
+
?err,
|
|
1512
|
+
"Failed to send close message to client"
|
|
1513
|
+
)
|
|
1514
|
+
}
|
|
1515
|
+
};
|
|
1516
|
+
|
|
1517
|
+
match client_sink.flush().await {
|
|
1518
|
+
Ok(_) => {
|
|
1519
|
+
tracing::trace!(
|
|
1520
|
+
"Successfully flushed client sink after close"
|
|
1521
|
+
)
|
|
1522
|
+
}
|
|
1523
|
+
Err(err) => {
|
|
1524
|
+
tracing::error!(
|
|
1525
|
+
?err,
|
|
1526
|
+
"Failed to flush client sink after close"
|
|
1527
|
+
)
|
|
1528
|
+
}
|
|
1529
|
+
};
|
|
1530
|
+
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
// Use backoff for the next attempt
|
|
1535
|
+
let backoff = Self::calculate_backoff(attempts, initial_interval);
|
|
1536
|
+
tracing::debug!(
|
|
1537
|
+
"Waiting for {:?} before next connection attempt",
|
|
1538
|
+
backoff
|
|
1539
|
+
);
|
|
1540
|
+
|
|
1541
|
+
tokio::time::sleep(backoff).await;
|
|
1542
|
+
|
|
1543
|
+
// Resolve target again, this time ignoring cache. This makes sure
|
|
1544
|
+
// we always re-fetch the route on error
|
|
1545
|
+
let new_target = state
|
|
1546
|
+
.resolve_route(
|
|
1547
|
+
&req_host,
|
|
1548
|
+
&req_path,
|
|
1549
|
+
&req_method,
|
|
1550
|
+
state.port_type.clone(),
|
|
1551
|
+
&req_headers,
|
|
1552
|
+
true,
|
|
1553
|
+
)
|
|
1554
|
+
.await;
|
|
1555
|
+
|
|
1556
|
+
match new_target {
|
|
1557
|
+
Ok(ResolveRouteOutput::Target(new_target)) => {
|
|
1558
|
+
target = new_target;
|
|
1559
|
+
}
|
|
1560
|
+
Ok(ResolveRouteOutput::Response(response)) => {
|
|
1561
|
+
tracing::debug!(
|
|
1562
|
+
status=?response.status,
|
|
1563
|
+
message=?response.message,
|
|
1564
|
+
docs=?response.docs,
|
|
1565
|
+
"got response instead of websocket target",
|
|
1566
|
+
);
|
|
1567
|
+
|
|
1568
|
+
// Close the WebSocket connection with the response message
|
|
1569
|
+
let _ = client_ws
|
|
1570
|
+
.close(Some(str_to_close_frame(response.message.as_ref())))
|
|
1571
|
+
.await;
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
Ok(ResolveRouteOutput::CustomServe(_)) => {
|
|
1575
|
+
let _ = client_ws
|
|
1576
|
+
.close(Some(err_to_close_frame(
|
|
1577
|
+
errors::WebSocketTargetChanged.build(),
|
|
1578
|
+
ray_id,
|
|
1579
|
+
)))
|
|
1580
|
+
.await;
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
Err(err) => {
|
|
1584
|
+
tracing::error!(?err, "Routing error");
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
// If we couldn't connect to the upstream server, exit the task
|
|
1590
|
+
let upstream_ws = match upstream_ws {
|
|
1591
|
+
Some(ws) => {
|
|
1592
|
+
tracing::debug!(
|
|
1593
|
+
"Successfully established upstream WebSocket connection"
|
|
1594
|
+
);
|
|
1595
|
+
ws
|
|
1596
|
+
}
|
|
1597
|
+
Option::None => {
|
|
1598
|
+
tracing::error!(
|
|
1599
|
+
"Failed to establish upstream WebSocket connection (unexpected)"
|
|
1600
|
+
);
|
|
1601
|
+
return; // Should never happen due to checks above, but just in case
|
|
1602
|
+
}
|
|
1603
|
+
};
|
|
1604
|
+
|
|
1605
|
+
// Now set up bidirectional communication between the client and upstream WebSockets
|
|
1606
|
+
tracing::debug!("Setting up bidirectional WebSocket proxying");
|
|
1607
|
+
let (client_sink, client_stream) = client_ws.split();
|
|
1608
|
+
let (upstream_sink, upstream_stream) = upstream_ws.split();
|
|
1609
|
+
|
|
1610
|
+
// Create channels for coordinating shutdown between client and upstream
|
|
1611
|
+
let (shutdown_tx, shutdown_rx) = tokio::sync::watch::channel(false);
|
|
1612
|
+
|
|
1613
|
+
// Manually forward messages from client to upstream server with shutdown coordination
|
|
1614
|
+
let client_to_upstream = async {
|
|
1615
|
+
tracing::debug!("Starting client-to-upstream forwarder");
|
|
1616
|
+
let mut stream = client_stream;
|
|
1617
|
+
let mut sink = upstream_sink;
|
|
1618
|
+
let mut shutdown_rx = shutdown_rx.clone();
|
|
1619
|
+
|
|
1620
|
+
loop {
|
|
1621
|
+
tokio::select! {
|
|
1622
|
+
// Check for shutdown signal
|
|
1623
|
+
shutdown_result = shutdown_rx.changed() => {
|
|
1624
|
+
match shutdown_result {
|
|
1625
|
+
Ok(_) => {
|
|
1626
|
+
if *shutdown_rx.borrow() {
|
|
1627
|
+
tracing::debug!("Client-to-upstream forwarder shutting down due to signal");
|
|
1628
|
+
break;
|
|
1629
|
+
}
|
|
1630
|
+
},
|
|
1631
|
+
Err(err) => {
|
|
1632
|
+
// Channel closed
|
|
1633
|
+
tracing::debug!(?err, "Client-to-upstream shutdown channel closed");
|
|
1634
|
+
break;
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
// Process next message from client
|
|
1640
|
+
msg_result = stream.next() => {
|
|
1641
|
+
match msg_result {
|
|
1642
|
+
Some(Ok(client_msg)) => {
|
|
1643
|
+
// Convert from hyper_tungstenite::Message to tokio_tungstenite::Message
|
|
1644
|
+
let upstream_msg = match client_msg {
|
|
1645
|
+
hyper_tungstenite::tungstenite::Message::Text(text) => {
|
|
1646
|
+
tokio_tungstenite::tungstenite::Message::Text(text)
|
|
1647
|
+
},
|
|
1648
|
+
hyper_tungstenite::tungstenite::Message::Binary(data) => {
|
|
1649
|
+
tokio_tungstenite::tungstenite::Message::Binary(data)
|
|
1650
|
+
},
|
|
1651
|
+
hyper_tungstenite::tungstenite::Message::Ping(data) => {
|
|
1652
|
+
tokio_tungstenite::tungstenite::Message::Ping(data)
|
|
1653
|
+
},
|
|
1654
|
+
hyper_tungstenite::tungstenite::Message::Pong(data) => {
|
|
1655
|
+
tokio_tungstenite::tungstenite::Message::Pong(data)
|
|
1656
|
+
},
|
|
1657
|
+
hyper_tungstenite::tungstenite::Message::Close(frame) => {
|
|
1658
|
+
// Signal shutdown to other direction
|
|
1659
|
+
let _ = shutdown_tx.send(true);
|
|
1660
|
+
|
|
1661
|
+
to_hyper_close(frame)
|
|
1662
|
+
},
|
|
1663
|
+
hyper_tungstenite::tungstenite::Message::Frame(_) => {
|
|
1664
|
+
// Skip frames - they're an implementation detail
|
|
1665
|
+
continue;
|
|
1666
|
+
},
|
|
1667
|
+
};
|
|
1668
|
+
|
|
1669
|
+
// Send the message with a timeout
|
|
1670
|
+
tracing::trace!("Sending message to upstream server");
|
|
1671
|
+
let send_result = tokio::time::timeout(
|
|
1672
|
+
Duration::from_secs(5),
|
|
1673
|
+
sink.send(upstream_msg)
|
|
1674
|
+
).await;
|
|
1675
|
+
|
|
1676
|
+
match send_result {
|
|
1677
|
+
Ok(Ok(_)) => {
|
|
1678
|
+
tracing::trace!("Message sent to upstream successfully");
|
|
1679
|
+
// Flush the sink with a timeout
|
|
1680
|
+
tracing::trace!("Flushing upstream sink");
|
|
1681
|
+
let flush_result = tokio::time::timeout(
|
|
1682
|
+
Duration::from_secs(2),
|
|
1683
|
+
sink.flush()
|
|
1684
|
+
).await;
|
|
1685
|
+
|
|
1686
|
+
if let Err(_) = flush_result {
|
|
1687
|
+
tracing::trace!("Timeout flushing upstream sink");
|
|
1688
|
+
let _ = shutdown_tx.send(true);
|
|
1689
|
+
break;
|
|
1690
|
+
} else if let Ok(Err(err)) = flush_result {
|
|
1691
|
+
tracing::trace!(?err, "Error flushing upstream sink");
|
|
1692
|
+
let _ = shutdown_tx.send(true);
|
|
1693
|
+
break;
|
|
1694
|
+
} else {
|
|
1695
|
+
tracing::trace!("Upstream sink flushed successfully");
|
|
1696
|
+
}
|
|
1697
|
+
},
|
|
1698
|
+
Ok(Err(err)) => {
|
|
1699
|
+
tracing::trace!(?err, "Error sending message to upstream");
|
|
1700
|
+
let _ = shutdown_tx.send(true);
|
|
1701
|
+
break;
|
|
1702
|
+
},
|
|
1703
|
+
Err(_) => {
|
|
1704
|
+
tracing::trace!("Timeout sending message to upstream after 5s");
|
|
1705
|
+
let _ = shutdown_tx.send(true);
|
|
1706
|
+
break;
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
},
|
|
1710
|
+
Some(Err(err)) => {
|
|
1711
|
+
// Error receiving message from client
|
|
1712
|
+
tracing::trace!(?err, "Error receiving message from client");
|
|
1713
|
+
tracing::trace!(?err, "Error details");
|
|
1714
|
+
// Signal shutdown to other direction
|
|
1715
|
+
let _ = shutdown_tx.send(true);
|
|
1716
|
+
break;
|
|
1717
|
+
},
|
|
1718
|
+
None => {
|
|
1719
|
+
// End of stream
|
|
1720
|
+
tracing::trace!("Client WebSocket stream ended");
|
|
1721
|
+
// Signal shutdown to other direction
|
|
1722
|
+
let _ = shutdown_tx.send(true);
|
|
1723
|
+
break;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
// Try to send a close frame - ignore errors as the connection might already be closed
|
|
1731
|
+
tracing::trace!("Attempting to send close message to upstream");
|
|
1732
|
+
match sink
|
|
1733
|
+
.send(tokio_tungstenite::tungstenite::Message::Close(None))
|
|
1734
|
+
.await
|
|
1735
|
+
{
|
|
1736
|
+
Ok(_) => {
|
|
1737
|
+
tracing::trace!("Close message sent to upstream successfully")
|
|
1738
|
+
}
|
|
1739
|
+
Err(err) => {
|
|
1740
|
+
tracing::trace!(
|
|
1741
|
+
?err,
|
|
1742
|
+
"Failed to send close message to upstream"
|
|
1743
|
+
)
|
|
1744
|
+
}
|
|
1745
|
+
};
|
|
1746
|
+
|
|
1747
|
+
match sink.flush().await {
|
|
1748
|
+
Ok(_) => {
|
|
1749
|
+
tracing::trace!(
|
|
1750
|
+
"Upstream sink flushed successfully after close"
|
|
1751
|
+
)
|
|
1752
|
+
}
|
|
1753
|
+
Err(err) => {
|
|
1754
|
+
tracing::trace!(
|
|
1755
|
+
?err,
|
|
1756
|
+
"Failed to flush upstream sink after close"
|
|
1757
|
+
)
|
|
1758
|
+
}
|
|
1759
|
+
};
|
|
1760
|
+
|
|
1761
|
+
tracing::debug!("Client-to-upstream task completed");
|
|
1762
|
+
};
|
|
1763
|
+
|
|
1764
|
+
// Manually forward messages from upstream server to client with shutdown coordination
|
|
1765
|
+
let upstream_to_client = async {
|
|
1766
|
+
tracing::debug!("Starting upstream-to-client forwarder");
|
|
1767
|
+
let mut stream = upstream_stream;
|
|
1768
|
+
let mut sink = client_sink;
|
|
1769
|
+
let mut shutdown_rx = shutdown_rx.clone();
|
|
1770
|
+
|
|
1771
|
+
loop {
|
|
1772
|
+
tokio::select! {
|
|
1773
|
+
// Check for shutdown signal
|
|
1774
|
+
shutdown_result = shutdown_rx.changed() => {
|
|
1775
|
+
match shutdown_result {
|
|
1776
|
+
Ok(_) => {
|
|
1777
|
+
if *shutdown_rx.borrow() {
|
|
1778
|
+
tracing::debug!("Upstream-to-client forwarder shutting down due to signal");
|
|
1779
|
+
break;
|
|
1780
|
+
}
|
|
1781
|
+
},
|
|
1782
|
+
Err(err) => {
|
|
1783
|
+
// Channel closed
|
|
1784
|
+
tracing::debug!(?err, "Upstream-to-client shutdown channel closed");
|
|
1785
|
+
break;
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
// Process next message from upstream
|
|
1791
|
+
msg_result = stream.next() => {
|
|
1792
|
+
match msg_result {
|
|
1793
|
+
Some(Ok(upstream_msg)) => {
|
|
1794
|
+
// Convert from tokio_tungstenite::Message to hyper_tungstenite::Message
|
|
1795
|
+
let client_msg = match upstream_msg {
|
|
1796
|
+
tokio_tungstenite::tungstenite::Message::Text(text) => {
|
|
1797
|
+
hyper_tungstenite::tungstenite::Message::Text(text)
|
|
1798
|
+
},
|
|
1799
|
+
tokio_tungstenite::tungstenite::Message::Binary(data) => {
|
|
1800
|
+
hyper_tungstenite::tungstenite::Message::Binary(data)
|
|
1801
|
+
},
|
|
1802
|
+
tokio_tungstenite::tungstenite::Message::Ping(data) => {
|
|
1803
|
+
hyper_tungstenite::tungstenite::Message::Ping(data)
|
|
1804
|
+
},
|
|
1805
|
+
tokio_tungstenite::tungstenite::Message::Pong(data) => {
|
|
1806
|
+
hyper_tungstenite::tungstenite::Message::Pong(data)
|
|
1807
|
+
},
|
|
1808
|
+
tokio_tungstenite::tungstenite::Message::Close(frame) => {
|
|
1809
|
+
// Signal shutdown to other direction
|
|
1810
|
+
let _ = shutdown_tx.send(true);
|
|
1811
|
+
|
|
1812
|
+
to_hyper_close(frame)
|
|
1813
|
+
},
|
|
1814
|
+
tokio_tungstenite::tungstenite::Message::Frame(_) => {
|
|
1815
|
+
// Skip frames - they're an implementation detail
|
|
1816
|
+
continue;
|
|
1817
|
+
},
|
|
1818
|
+
};
|
|
1819
|
+
|
|
1820
|
+
// Send the message with a timeout
|
|
1821
|
+
tracing::trace!("Sending message to client");
|
|
1822
|
+
let send_result = tokio::time::timeout(
|
|
1823
|
+
Duration::from_secs(5),
|
|
1824
|
+
sink.send(client_msg)
|
|
1825
|
+
).await;
|
|
1826
|
+
|
|
1827
|
+
match send_result {
|
|
1828
|
+
Ok(Ok(_)) => {
|
|
1829
|
+
tracing::trace!("Message sent to client successfully");
|
|
1830
|
+
// Flush the sink with a timeout
|
|
1831
|
+
tracing::trace!("Flushing client sink");
|
|
1832
|
+
let flush_result = tokio::time::timeout(
|
|
1833
|
+
Duration::from_secs(2),
|
|
1834
|
+
sink.flush()
|
|
1835
|
+
).await;
|
|
1836
|
+
|
|
1837
|
+
if let Err(_) = flush_result {
|
|
1838
|
+
tracing::trace!("Timeout flushing client sink");
|
|
1839
|
+
let _ = shutdown_tx.send(true);
|
|
1840
|
+
break;
|
|
1841
|
+
} else if let Ok(Err(err)) = flush_result {
|
|
1842
|
+
tracing::trace!(?err, "Error flushing client sink");
|
|
1843
|
+
let _ = shutdown_tx.send(true);
|
|
1844
|
+
break;
|
|
1845
|
+
} else {
|
|
1846
|
+
tracing::trace!("Client sink flushed successfully");
|
|
1847
|
+
}
|
|
1848
|
+
},
|
|
1849
|
+
Ok(Err(err)) => {
|
|
1850
|
+
tracing::trace!(?err, "Error sending message to client");
|
|
1851
|
+
let _ = shutdown_tx.send(true);
|
|
1852
|
+
break;
|
|
1853
|
+
},
|
|
1854
|
+
Err(_) => {
|
|
1855
|
+
tracing::trace!("Timeout sending message to client after 5s");
|
|
1856
|
+
let _ = shutdown_tx.send(true);
|
|
1857
|
+
break;
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
},
|
|
1861
|
+
Some(Err(err)) => {
|
|
1862
|
+
// Error receiving message from upstream
|
|
1863
|
+
tracing::trace!(?err, "Error receiving message from upstream");
|
|
1864
|
+
// Signal shutdown to other direction
|
|
1865
|
+
let _ = shutdown_tx.send(true);
|
|
1866
|
+
break;
|
|
1867
|
+
},
|
|
1868
|
+
None => {
|
|
1869
|
+
// End of stream
|
|
1870
|
+
tracing::trace!("Upstream WebSocket stream ended");
|
|
1871
|
+
// Signal shutdown to other direction
|
|
1872
|
+
let _ = shutdown_tx.send(true);
|
|
1873
|
+
break;
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
// Try to send a close frame - ignore errors as the connection might already be closed
|
|
1881
|
+
tracing::trace!("Attempting to send close message to client");
|
|
1882
|
+
match sink.send(to_hyper_close(None)).await {
|
|
1883
|
+
Ok(_) => {
|
|
1884
|
+
tracing::trace!("Close message sent to client successfully")
|
|
1885
|
+
}
|
|
1886
|
+
Err(err) => {
|
|
1887
|
+
tracing::trace!(?err, "Failed to send close message to client")
|
|
1888
|
+
}
|
|
1889
|
+
};
|
|
1890
|
+
|
|
1891
|
+
match sink.flush().await {
|
|
1892
|
+
Ok(_) => {
|
|
1893
|
+
tracing::trace!("Client sink flushed successfully after close")
|
|
1894
|
+
}
|
|
1895
|
+
Err(err) => {
|
|
1896
|
+
tracing::trace!(?err, "Failed to flush client sink after close")
|
|
1897
|
+
}
|
|
1898
|
+
};
|
|
1899
|
+
|
|
1900
|
+
tracing::trace!("Upstream-to-client task completed");
|
|
1901
|
+
};
|
|
1902
|
+
|
|
1903
|
+
// Run both directions concurrently until either one completes or errors
|
|
1904
|
+
tracing::debug!("Starting bidirectional message forwarding");
|
|
1905
|
+
tokio::join!(client_to_upstream, upstream_to_client);
|
|
1906
|
+
tracing::debug!("Bidirectional message forwarding completed");
|
|
1907
|
+
}
|
|
1908
|
+
.instrument(tracing::info_span!("handle_ws_task_target")),
|
|
1909
|
+
);
|
|
1910
|
+
}
|
|
1911
|
+
ResolveRouteOutput::Response(_) => unreachable!(),
|
|
1912
|
+
ResolveRouteOutput::CustomServe(mut handler) => {
|
|
1913
|
+
tracing::debug!(%req_path, "Spawning task to handle WebSocket communication");
|
|
1914
|
+
let mut request_context = request_context.clone();
|
|
1915
|
+
let req_headers = req_headers.clone();
|
|
1916
|
+
let state = self.state.clone();
|
|
1917
|
+
let req_path = req_path.clone();
|
|
1918
|
+
let req_host = req_host.clone();
|
|
1919
|
+
let req_method = req_method.clone();
|
|
1920
|
+
|
|
1921
|
+
self.state.tasks.spawn(
|
|
1922
|
+
async move {
|
|
1923
|
+
let request_id = match state
|
|
1924
|
+
.acquire_in_flight(client_ip, &actor_id, &req_headers)
|
|
1925
|
+
.await?
|
|
1926
|
+
{
|
|
1927
|
+
Some(id) => id,
|
|
1928
|
+
None => {
|
|
1929
|
+
return Err(errors::RateLimit {
|
|
1930
|
+
actor_id,
|
|
1931
|
+
method: req_method.to_string(),
|
|
1932
|
+
path: req_path.clone(),
|
|
1933
|
+
ip: client_ip.to_string(),
|
|
1934
|
+
}
|
|
1935
|
+
.build()
|
|
1936
|
+
.into());
|
|
1937
|
+
}
|
|
1938
|
+
};
|
|
1939
|
+
let mut ws_hibernation_close = false;
|
|
1940
|
+
let mut after_hibernation = false;
|
|
1941
|
+
let mut attempts = 0u32;
|
|
1942
|
+
|
|
1943
|
+
let ws_handle = WebSocketHandle::new(client_ws)
|
|
1944
|
+
.await
|
|
1945
|
+
.context("failed initiating websocket handle")?;
|
|
1946
|
+
|
|
1947
|
+
loop {
|
|
1948
|
+
match handler
|
|
1949
|
+
.handle_websocket(
|
|
1950
|
+
ws_handle.clone(),
|
|
1951
|
+
&req_headers,
|
|
1952
|
+
&req_path,
|
|
1953
|
+
&mut request_context,
|
|
1954
|
+
request_id,
|
|
1955
|
+
after_hibernation,
|
|
1956
|
+
)
|
|
1957
|
+
.await
|
|
1958
|
+
{
|
|
1959
|
+
Ok(close_frame) => {
|
|
1960
|
+
tracing::debug!("websocket handler complete, closing");
|
|
1961
|
+
|
|
1962
|
+
// Send graceful close. This may fail if client already sent
|
|
1963
|
+
// close frame, which is normal.
|
|
1964
|
+
tracing::debug!(?close_frame, "sending close frame to client");
|
|
1965
|
+
match ws_handle.send(to_hyper_close(close_frame)).await {
|
|
1966
|
+
Ok(_) => {
|
|
1967
|
+
tracing::debug!("close frame sent successfully");
|
|
1968
|
+
}
|
|
1969
|
+
Err(err) => {
|
|
1970
|
+
tracing::debug!(
|
|
1971
|
+
?err,
|
|
1972
|
+
"failed to send close frame (websocket may be already closing)"
|
|
1973
|
+
);
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
// Flush to ensure close frame is sent
|
|
1978
|
+
tracing::debug!("flushing websocket");
|
|
1979
|
+
match ws_handle.flush().await {
|
|
1980
|
+
Ok(_) => {
|
|
1981
|
+
tracing::debug!("websocket flushed successfully");
|
|
1982
|
+
}
|
|
1983
|
+
Err(err) => {
|
|
1984
|
+
tracing::debug!(
|
|
1985
|
+
?err,
|
|
1986
|
+
"failed to flush websocket (websocket may be already closing)"
|
|
1987
|
+
);
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
// Keep TCP connection open briefly to allow client to process close
|
|
1992
|
+
tokio::time::sleep(WEBSOCKET_CLOSE_LINGER).await;
|
|
1993
|
+
|
|
1994
|
+
break;
|
|
1995
|
+
}
|
|
1996
|
+
Err(err) => {
|
|
1997
|
+
tracing::debug!(?err, "websocket handler error");
|
|
1998
|
+
|
|
1999
|
+
// Denotes that the connection did not fail, but the downstream has closed
|
|
2000
|
+
let ws_hibernate = is_ws_hibernate(&err);
|
|
2001
|
+
|
|
2002
|
+
if ws_hibernate {
|
|
2003
|
+
attempts = 0;
|
|
2004
|
+
} else {
|
|
2005
|
+
attempts += 1;
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
if ws_hibernate {
|
|
2009
|
+
// This should be unreachable because as soon as the actor is
|
|
2010
|
+
// reconnected to after hibernation the gateway will consume the close
|
|
2011
|
+
// frame from the client ws stream
|
|
2012
|
+
ensure!(
|
|
2013
|
+
!ws_hibernation_close,
|
|
2014
|
+
"should not be hibernating again after receiving a close frame during hibernation"
|
|
2015
|
+
);
|
|
2016
|
+
|
|
2017
|
+
// After this function returns:
|
|
2018
|
+
// - the route will be resolved again
|
|
2019
|
+
// - the websocket will connect to the new downstream target
|
|
2020
|
+
// - the gateway will continue reading messages from the client ws
|
|
2021
|
+
// (starting with the message that caused the hibernation to end)
|
|
2022
|
+
let res = handler
|
|
2023
|
+
.handle_websocket_hibernation(
|
|
2024
|
+
ws_handle.clone(),
|
|
2025
|
+
request_id,
|
|
2026
|
+
)
|
|
2027
|
+
.await?;
|
|
2028
|
+
|
|
2029
|
+
after_hibernation = true;
|
|
2030
|
+
|
|
2031
|
+
// Despite receiving a close frame from the client during hibernation
|
|
2032
|
+
// we are going to reconnect to the actor so that it knows the
|
|
2033
|
+
// connection has closed
|
|
2034
|
+
if let HibernationResult::Close = res {
|
|
2035
|
+
tracing::debug!("starting hibernating websocket close");
|
|
2036
|
+
|
|
2037
|
+
ws_hibernation_close = true;
|
|
2038
|
+
}
|
|
2039
|
+
} else if attempts > max_attempts
|
|
2040
|
+
|| !is_retryable_ws_error(&err)
|
|
2041
|
+
{
|
|
2042
|
+
tracing::debug!(
|
|
2043
|
+
?attempts,
|
|
2044
|
+
?max_attempts,
|
|
2045
|
+
"WebSocket failed"
|
|
2046
|
+
);
|
|
2047
|
+
|
|
2048
|
+
// Close WebSocket with error
|
|
2049
|
+
ws_handle
|
|
2050
|
+
.send(to_hyper_close(Some(err_to_close_frame(
|
|
2051
|
+
err, ray_id,
|
|
2052
|
+
))))
|
|
2053
|
+
.await?;
|
|
2054
|
+
|
|
2055
|
+
// Flush to ensure close frame is sent
|
|
2056
|
+
ws_handle.flush().await?;
|
|
2057
|
+
|
|
2058
|
+
// Keep TCP connection open briefly to allow client to process close
|
|
2059
|
+
tokio::time::sleep(WEBSOCKET_CLOSE_LINGER).await;
|
|
2060
|
+
|
|
2061
|
+
break;
|
|
2062
|
+
} else {
|
|
2063
|
+
let backoff = ProxyService::calculate_backoff(
|
|
2064
|
+
attempts,
|
|
2065
|
+
initial_interval,
|
|
2066
|
+
);
|
|
2067
|
+
|
|
2068
|
+
tracing::debug!(
|
|
2069
|
+
?backoff,
|
|
2070
|
+
"WebSocket attempt {attempts} failed (service unavailable)"
|
|
2071
|
+
);
|
|
2072
|
+
|
|
2073
|
+
// Apply backoff for retryable error
|
|
2074
|
+
tokio::time::sleep(backoff).await;
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
// Retry route resolution
|
|
2078
|
+
match state
|
|
2079
|
+
.resolve_route(
|
|
2080
|
+
&req_host,
|
|
2081
|
+
&req_path,
|
|
2082
|
+
&req_method,
|
|
2083
|
+
state.port_type.clone(),
|
|
2084
|
+
&req_headers,
|
|
2085
|
+
true,
|
|
2086
|
+
)
|
|
2087
|
+
.await
|
|
2088
|
+
{
|
|
2089
|
+
Ok(ResolveRouteOutput::CustomServe(new_handler)) => {
|
|
2090
|
+
handler = new_handler;
|
|
2091
|
+
continue;
|
|
2092
|
+
}
|
|
2093
|
+
Ok(ResolveRouteOutput::Response(response)) => {
|
|
2094
|
+
ws_handle
|
|
2095
|
+
.send(to_hyper_close(Some(str_to_close_frame(
|
|
2096
|
+
response.message.as_ref(),
|
|
2097
|
+
))))
|
|
2098
|
+
.await?;
|
|
2099
|
+
|
|
2100
|
+
// Flush to ensure close frame is sent
|
|
2101
|
+
ws_handle.flush().await?;
|
|
2102
|
+
|
|
2103
|
+
// Keep TCP connection open briefly to allow client to process close
|
|
2104
|
+
tokio::time::sleep(WEBSOCKET_CLOSE_LINGER).await;
|
|
2105
|
+
}
|
|
2106
|
+
Ok(ResolveRouteOutput::Target(_)) => {
|
|
2107
|
+
ws_handle
|
|
2108
|
+
.send(to_hyper_close(Some(err_to_close_frame(
|
|
2109
|
+
errors::WebSocketTargetChanged.build(),
|
|
2110
|
+
ray_id,
|
|
2111
|
+
))))
|
|
2112
|
+
.await?;
|
|
2113
|
+
|
|
2114
|
+
// Flush to ensure close frame is sent
|
|
2115
|
+
ws_handle.flush().await?;
|
|
2116
|
+
|
|
2117
|
+
// Keep TCP connection open briefly to allow client to process close
|
|
2118
|
+
tokio::time::sleep(WEBSOCKET_CLOSE_LINGER).await;
|
|
2119
|
+
|
|
2120
|
+
break;
|
|
2121
|
+
}
|
|
2122
|
+
Err(err) => {
|
|
2123
|
+
ws_handle
|
|
2124
|
+
.send(to_hyper_close(Some(err_to_close_frame(
|
|
2125
|
+
err, ray_id,
|
|
2126
|
+
))))
|
|
2127
|
+
.await?;
|
|
2128
|
+
|
|
2129
|
+
// Flush to ensure close frame is sent
|
|
2130
|
+
ws_handle.flush().await?;
|
|
2131
|
+
|
|
2132
|
+
// Keep TCP connection open briefly to allow client to process close
|
|
2133
|
+
tokio::time::sleep(WEBSOCKET_CLOSE_LINGER).await;
|
|
2134
|
+
|
|
2135
|
+
break;
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
// Release in-flight counter and request ID when task completes
|
|
2143
|
+
state
|
|
2144
|
+
.release_in_flight(client_ip, &actor_id, request_id)
|
|
2145
|
+
.await;
|
|
2146
|
+
|
|
2147
|
+
Ok(())
|
|
2148
|
+
}
|
|
2149
|
+
.instrument(tracing::info_span!("handle_ws_task_custom_serve")),
|
|
2150
|
+
);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
// Return the response that will upgrade the client connection
|
|
2155
|
+
// For proper WebSocket handshaking, we need to preserve the original response
|
|
2156
|
+
// structure but convert it to our expected return type without modifying its content
|
|
2157
|
+
tracing::debug!("Returning WebSocket upgrade response to client");
|
|
2158
|
+
// Extract the parts from the response but preserve all headers and status
|
|
2159
|
+
let (mut parts, _) = client_response.into_parts();
|
|
2160
|
+
|
|
2161
|
+
// Add Sec-WebSocket-Protocol header to the response
|
|
2162
|
+
// Many WebSocket clients (e.g. node-ws & Cloudflare) require a protocol in the response
|
|
2163
|
+
parts.headers.insert(
|
|
2164
|
+
"sec-websocket-protocol",
|
|
2165
|
+
hyper::header::HeaderValue::from_static("rivet"),
|
|
2166
|
+
);
|
|
2167
|
+
|
|
2168
|
+
// Create a new response with an empty body - WebSocket upgrades don't need a body
|
|
2169
|
+
Ok(Response::from_parts(
|
|
2170
|
+
parts,
|
|
2171
|
+
ResponseBody::Full(Full::<Bytes>::new(Bytes::new())),
|
|
2172
|
+
))
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
impl ProxyService {
|
|
2177
|
+
// Process an individual request
|
|
2178
|
+
#[tracing::instrument(name = "guard_request", skip_all, fields(ray_id, req_id))]
|
|
2179
|
+
pub async fn process(&self, mut req: Request<BodyIncoming>) -> Result<Response<ResponseBody>> {
|
|
2180
|
+
let start_time = Instant::now();
|
|
2181
|
+
|
|
2182
|
+
let request_ids = RequestIds::new(self.state.config.dc_label());
|
|
2183
|
+
req.extensions_mut().insert(request_ids);
|
|
2184
|
+
|
|
2185
|
+
tracing::Span::current()
|
|
2186
|
+
.record("req_id", request_ids.req_id.to_string())
|
|
2187
|
+
.record("ray_id", request_ids.ray_id.to_string());
|
|
2188
|
+
|
|
2189
|
+
// Create request context for analytics tracking
|
|
2190
|
+
let mut request_context =
|
|
2191
|
+
RequestContext::new(self.state.clickhouse_inserter.clone(), request_ids);
|
|
2192
|
+
|
|
2193
|
+
// Extract request information for logging and analytics before consuming the request
|
|
2194
|
+
let incoming_ray_id = req
|
|
2195
|
+
.headers()
|
|
2196
|
+
.get(X_RIVET_RAY_ID)
|
|
2197
|
+
.and_then(|h| h.to_str().ok())
|
|
2198
|
+
.and_then(|id| Id::parse(id).ok());
|
|
2199
|
+
let host = req
|
|
2200
|
+
.headers()
|
|
2201
|
+
.get(hyper::header::HOST)
|
|
2202
|
+
.and_then(|h| h.to_str().ok())
|
|
2203
|
+
.unwrap_or("unknown")
|
|
2204
|
+
.to_string();
|
|
2205
|
+
let uri_string = req.uri().to_string();
|
|
2206
|
+
let path = req
|
|
2207
|
+
.uri()
|
|
2208
|
+
.path_and_query()
|
|
2209
|
+
.map(|x| x.to_string())
|
|
2210
|
+
.unwrap_or_else(|| req.uri().path().to_string());
|
|
2211
|
+
let method = req.method().clone();
|
|
2212
|
+
|
|
2213
|
+
let user_agent = req
|
|
2214
|
+
.headers()
|
|
2215
|
+
.get(hyper::header::USER_AGENT)
|
|
2216
|
+
.and_then(|h| h.to_str().ok())
|
|
2217
|
+
.map(|s| s.to_string());
|
|
2218
|
+
|
|
2219
|
+
// Populate request context with available data
|
|
2220
|
+
request_context.client_ip = Some(self.remote_addr.ip());
|
|
2221
|
+
request_context.client_request_host = Some(host.clone());
|
|
2222
|
+
request_context.client_request_method = Some(method.to_string());
|
|
2223
|
+
request_context.client_request_path = Some(req.uri().path().to_string());
|
|
2224
|
+
request_context.client_request_protocol = Some(format!("{:?}", req.version()));
|
|
2225
|
+
request_context.client_request_scheme =
|
|
2226
|
+
Some(req.uri().scheme_str().unwrap_or("http").to_string());
|
|
2227
|
+
request_context.client_request_uri = Some(path.clone());
|
|
2228
|
+
request_context.client_src_port = Some(self.remote_addr.port());
|
|
2229
|
+
|
|
2230
|
+
if let Some(referer) = req
|
|
2231
|
+
.headers()
|
|
2232
|
+
.get(hyper::header::REFERER)
|
|
2233
|
+
.and_then(|h| h.to_str().ok())
|
|
2234
|
+
{
|
|
2235
|
+
request_context.client_request_referer = Some(referer.to_string());
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
if let Some(ua) = &user_agent {
|
|
2239
|
+
request_context.client_request_user_agent = Some(ua.clone());
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
if let Some(requested_with) = req
|
|
2243
|
+
.headers()
|
|
2244
|
+
.get("x-requested-with")
|
|
2245
|
+
.and_then(|h| h.to_str().ok())
|
|
2246
|
+
{
|
|
2247
|
+
request_context.client_x_requested_with = Some(requested_with.to_string());
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
// TLS information would be set here if available (for HTTPS connections)
|
|
2251
|
+
// This requires TLS connection introspection and is marked for future enhancement
|
|
2252
|
+
|
|
2253
|
+
// Debug log request information with structured fields (Apache-like access log)
|
|
2254
|
+
tracing::debug!(
|
|
2255
|
+
?incoming_ray_id,
|
|
2256
|
+
ray_id=?request_ids.ray_id,
|
|
2257
|
+
req_id=?request_ids.req_id,
|
|
2258
|
+
method = %method,
|
|
2259
|
+
path = %path,
|
|
2260
|
+
host = %host,
|
|
2261
|
+
remote_addr = %self.remote_addr,
|
|
2262
|
+
uri = %uri_string,
|
|
2263
|
+
protocol = ?self.state.port_type,
|
|
2264
|
+
user_agent = ?user_agent,
|
|
2265
|
+
"Request received"
|
|
2266
|
+
);
|
|
2267
|
+
|
|
2268
|
+
let is_websocket = hyper_tungstenite::is_upgrade_request(&req);
|
|
2269
|
+
|
|
2270
|
+
// Used for ws error proxying later
|
|
2271
|
+
let mut mock_req_builder = Request::builder()
|
|
2272
|
+
.method(req.method().clone())
|
|
2273
|
+
.uri(req.uri().clone())
|
|
2274
|
+
.version(req.version().clone());
|
|
2275
|
+
if let Some(headers) = mock_req_builder.headers_mut() {
|
|
2276
|
+
*headers = req.headers().clone();
|
|
2277
|
+
}
|
|
2278
|
+
if let Some(extensions) = mock_req_builder.extensions_mut() {
|
|
2279
|
+
*extensions = req.extensions().clone();
|
|
2280
|
+
}
|
|
2281
|
+
let mock_req = mock_req_builder.body(())?;
|
|
2282
|
+
|
|
2283
|
+
// Process the request
|
|
2284
|
+
let mut res = match self
|
|
2285
|
+
.handle_request(req, start_time, &mut request_context)
|
|
2286
|
+
.await
|
|
2287
|
+
{
|
|
2288
|
+
Ok(res) => res,
|
|
2289
|
+
Err(err) => {
|
|
2290
|
+
// Log the error
|
|
2291
|
+
tracing::error!(?err, "Request failed");
|
|
2292
|
+
|
|
2293
|
+
metrics::PROXY_REQUEST_ERROR
|
|
2294
|
+
.add(1, &[KeyValue::new("error_type", err.to_string())]);
|
|
2295
|
+
|
|
2296
|
+
// If we receive an error during a websocket request, we attempt to open the websocket anyway
|
|
2297
|
+
// so we can send the error via websocket instead of http. Most websocket clients don't handle
|
|
2298
|
+
// HTTP errors in a meaningful way resulting in unhelpful errors for the user
|
|
2299
|
+
if is_websocket {
|
|
2300
|
+
tracing::debug!("Upgrading client connection to WebSocket for error proxy");
|
|
2301
|
+
match hyper_tungstenite::upgrade(mock_req, None) {
|
|
2302
|
+
Ok((client_response, client_ws)) => {
|
|
2303
|
+
tracing::debug!("Client WebSocket upgrade for error proxy successful");
|
|
2304
|
+
|
|
2305
|
+
self.state.tasks.spawn(
|
|
2306
|
+
async move {
|
|
2307
|
+
let ws_handle = match WebSocketHandle::new(client_ws).await {
|
|
2308
|
+
Ok(ws_handle) => ws_handle,
|
|
2309
|
+
Err(err) => {
|
|
2310
|
+
tracing::debug!(
|
|
2311
|
+
?err,
|
|
2312
|
+
"failed initiating websocket handle for error proxy"
|
|
2313
|
+
);
|
|
2314
|
+
return;
|
|
2315
|
+
}
|
|
2316
|
+
};
|
|
2317
|
+
let frame = err_to_close_frame(err, Some(request_ids.ray_id));
|
|
2318
|
+
|
|
2319
|
+
// Manual conversion to handle different tungstenite versions
|
|
2320
|
+
let code_num: u16 = frame.code.into();
|
|
2321
|
+
let reason = frame.reason.clone();
|
|
2322
|
+
|
|
2323
|
+
if let Err(err) = ws_handle
|
|
2324
|
+
.send(tokio_tungstenite::tungstenite::Message::Close(Some(
|
|
2325
|
+
tokio_tungstenite::tungstenite::protocol::CloseFrame {
|
|
2326
|
+
code: code_num.into(),
|
|
2327
|
+
reason,
|
|
2328
|
+
},
|
|
2329
|
+
)))
|
|
2330
|
+
.await
|
|
2331
|
+
{
|
|
2332
|
+
tracing::debug!(
|
|
2333
|
+
?err,
|
|
2334
|
+
"failed sending websocket error proxy"
|
|
2335
|
+
);
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
.instrument(
|
|
2339
|
+
tracing::info_span!("ws_error_proxy_task", ?request_ids.ray_id),
|
|
2340
|
+
),
|
|
2341
|
+
);
|
|
2342
|
+
|
|
2343
|
+
// Return the response that will upgrade the client connection
|
|
2344
|
+
// For proper WebSocket handshaking, we need to preserve the original response
|
|
2345
|
+
// structure but convert it to our expected return type without modifying its content
|
|
2346
|
+
tracing::debug!(
|
|
2347
|
+
"Returning WebSocket upgrade response for error proxy to client"
|
|
2348
|
+
);
|
|
2349
|
+
// Extract the parts from the response but preserve all headers and status
|
|
2350
|
+
let (mut parts, _) = client_response.into_parts();
|
|
2351
|
+
|
|
2352
|
+
// Add Sec-WebSocket-Protocol header to the response
|
|
2353
|
+
// Many WebSocket clients (e.g. node-ws & Cloudflare) require a protocol in the response
|
|
2354
|
+
parts.headers.insert(
|
|
2355
|
+
"sec-websocket-protocol",
|
|
2356
|
+
hyper::header::HeaderValue::from_static("rivet"),
|
|
2357
|
+
);
|
|
2358
|
+
|
|
2359
|
+
// Create a new response with an empty body - WebSocket upgrades don't need a body
|
|
2360
|
+
Response::from_parts(
|
|
2361
|
+
parts,
|
|
2362
|
+
ResponseBody::Full(Full::<Bytes>::new(Bytes::new())),
|
|
2363
|
+
)
|
|
2364
|
+
}
|
|
2365
|
+
Err(err) => {
|
|
2366
|
+
tracing::error!(
|
|
2367
|
+
?err,
|
|
2368
|
+
"Failed to upgrade client WebSocket for error proxy"
|
|
2369
|
+
);
|
|
2370
|
+
|
|
2371
|
+
err_into_response(
|
|
2372
|
+
errors::ConnectionError {
|
|
2373
|
+
error_message: format!(
|
|
2374
|
+
"Failed to upgrade client WebSocket for error proxy: {}",
|
|
2375
|
+
err
|
|
2376
|
+
),
|
|
2377
|
+
remote_addr: self.remote_addr.to_string(),
|
|
2378
|
+
}
|
|
2379
|
+
.build(),
|
|
2380
|
+
)?
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
} else {
|
|
2384
|
+
err_into_response(err)?
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
};
|
|
2388
|
+
|
|
2389
|
+
if is_websocket && res.status() != StatusCode::SWITCHING_PROTOCOLS {
|
|
2390
|
+
tracing::debug!("returned non-101 response to websocket");
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
// Add ray_id to response headers
|
|
2394
|
+
if let Ok(ray_id_value) = request_ids.ray_id.to_string().parse() {
|
|
2395
|
+
if let Some(existing_ray_id_value) = res
|
|
2396
|
+
.headers()
|
|
2397
|
+
.get(X_RIVET_RAY_ID)
|
|
2398
|
+
.and_then(|h| h.to_str().ok())
|
|
2399
|
+
{
|
|
2400
|
+
if ray_id_value != existing_ray_id_value {
|
|
2401
|
+
tracing::warn!(
|
|
2402
|
+
expected_ray_id=%request_ids.ray_id,
|
|
2403
|
+
received_ray_id=%existing_ray_id_value,
|
|
2404
|
+
"downstream service set ray id header to a different value",
|
|
2405
|
+
);
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
res.headers_mut().insert(X_RIVET_RAY_ID, ray_id_value);
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
let status = res.status().as_u16();
|
|
2413
|
+
|
|
2414
|
+
// Update request context with response details
|
|
2415
|
+
request_context.guard_response_status = Some(status);
|
|
2416
|
+
request_context.service_response_status = Some(status);
|
|
2417
|
+
|
|
2418
|
+
if let Some(content_type) = res
|
|
2419
|
+
.headers()
|
|
2420
|
+
.get(hyper::header::CONTENT_TYPE)
|
|
2421
|
+
.and_then(|h| h.to_str().ok())
|
|
2422
|
+
{
|
|
2423
|
+
request_context.guard_response_content_type = Some(content_type.to_string());
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
if let Some(expires) = res
|
|
2427
|
+
.headers()
|
|
2428
|
+
.get(hyper::header::EXPIRES)
|
|
2429
|
+
.and_then(|h| h.to_str().ok())
|
|
2430
|
+
{
|
|
2431
|
+
request_context.service_response_http_expires = Some(expires.to_string());
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
if let Some(last_modified) = res
|
|
2435
|
+
.headers()
|
|
2436
|
+
.get(hyper::header::LAST_MODIFIED)
|
|
2437
|
+
.and_then(|h| h.to_str().ok())
|
|
2438
|
+
{
|
|
2439
|
+
request_context.service_response_http_last_modified = Some(last_modified.to_string());
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
// Set timing information
|
|
2443
|
+
request_context.service_response_duration_ms =
|
|
2444
|
+
Some(start_time.elapsed().as_millis() as u32);
|
|
2445
|
+
|
|
2446
|
+
// Insert analytics event asynchronously
|
|
2447
|
+
let mut context_clone = request_context.clone();
|
|
2448
|
+
tokio::spawn(
|
|
2449
|
+
async move {
|
|
2450
|
+
if let Err(error) = context_clone.insert_event().await {
|
|
2451
|
+
tracing::warn!(?error, "failed to insert guard analytics event");
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
.instrument(tracing::info_span!("insert_event_task")),
|
|
2455
|
+
);
|
|
2456
|
+
|
|
2457
|
+
let content_length = res
|
|
2458
|
+
.headers()
|
|
2459
|
+
.get(hyper::header::CONTENT_LENGTH)
|
|
2460
|
+
.and_then(|h| h.to_str().ok())
|
|
2461
|
+
.and_then(|s| s.parse::<usize>().ok())
|
|
2462
|
+
.unwrap_or(0);
|
|
2463
|
+
|
|
2464
|
+
// Log information about the completed request
|
|
2465
|
+
tracing::debug!(
|
|
2466
|
+
?incoming_ray_id,
|
|
2467
|
+
ray_id=?request_ids.ray_id,
|
|
2468
|
+
req_id=?request_ids.req_id,
|
|
2469
|
+
method = %method,
|
|
2470
|
+
path = %path,
|
|
2471
|
+
host = %host,
|
|
2472
|
+
remote_addr = %self.remote_addr,
|
|
2473
|
+
status = %status,
|
|
2474
|
+
content_length = %content_length,
|
|
2475
|
+
"Request completed"
|
|
2476
|
+
);
|
|
2477
|
+
|
|
2478
|
+
Ok(res)
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
impl Clone for ProxyService {
|
|
2483
|
+
fn clone(&self) -> Self {
|
|
2484
|
+
Self {
|
|
2485
|
+
state: self.state.clone(),
|
|
2486
|
+
remote_addr: self.remote_addr,
|
|
2487
|
+
client: self.client.clone(),
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
// Factory for creating proxy services
|
|
2493
|
+
pub struct ProxyServiceFactory {
|
|
2494
|
+
state: Arc<ProxyState>,
|
|
2495
|
+
}
|
|
2496
|
+
|
|
2497
|
+
impl ProxyServiceFactory {
|
|
2498
|
+
pub fn new(
|
|
2499
|
+
config: rivet_config::Config,
|
|
2500
|
+
routing_fn: RoutingFn,
|
|
2501
|
+
cache_key_fn: CacheKeyFn,
|
|
2502
|
+
middleware_fn: MiddlewareFn,
|
|
2503
|
+
port_type: PortType,
|
|
2504
|
+
clickhouse_inserter: Option<clickhouse_inserter::ClickHouseInserterHandle>,
|
|
2505
|
+
) -> Self {
|
|
2506
|
+
let state = Arc::new(ProxyState::new(
|
|
2507
|
+
config,
|
|
2508
|
+
routing_fn,
|
|
2509
|
+
cache_key_fn,
|
|
2510
|
+
middleware_fn,
|
|
2511
|
+
port_type,
|
|
2512
|
+
clickhouse_inserter,
|
|
2513
|
+
));
|
|
2514
|
+
Self { state }
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
// Create a new proxy service for the given remote address
|
|
2518
|
+
pub fn create_service(&self, remote_addr: SocketAddr) -> ProxyService {
|
|
2519
|
+
ProxyService::new(self.state.clone(), remote_addr)
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2522
|
+
pub async fn wait_idle(&self) {
|
|
2523
|
+
self.state.tasks.wait_idle().await
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
fn add_proxy_headers_with_addr(
|
|
2528
|
+
headers: &mut hyper::HeaderMap,
|
|
2529
|
+
original_headers: &hyper::HeaderMap,
|
|
2530
|
+
remote_addr: SocketAddr,
|
|
2531
|
+
) -> Result<()> {
|
|
2532
|
+
// Copy headers except Host
|
|
2533
|
+
for (key, value) in original_headers.iter() {
|
|
2534
|
+
if key != hyper::header::HOST {
|
|
2535
|
+
headers.insert(key.clone(), value.clone());
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
// Add X-Forwarded-For header
|
|
2540
|
+
if let Some(existing) = original_headers.get(X_FORWARDED_FOR) {
|
|
2541
|
+
if let Ok(forwarded) = existing.to_str() {
|
|
2542
|
+
if !forwarded.contains(&remote_addr.ip().to_string()) {
|
|
2543
|
+
headers.insert(
|
|
2544
|
+
X_FORWARDED_FOR,
|
|
2545
|
+
hyper::header::HeaderValue::from_str(&format!(
|
|
2546
|
+
"{}, {}",
|
|
2547
|
+
forwarded,
|
|
2548
|
+
remote_addr.ip()
|
|
2549
|
+
))?,
|
|
2550
|
+
);
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
} else {
|
|
2554
|
+
headers.insert(
|
|
2555
|
+
X_FORWARDED_FOR,
|
|
2556
|
+
hyper::header::HeaderValue::from_str(&remote_addr.ip().to_string())?,
|
|
2557
|
+
);
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2560
|
+
Ok(())
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
fn err_into_response(err: anyhow::Error) -> Result<Response<ResponseBody>> {
|
|
2564
|
+
let (status, error_response) =
|
|
2565
|
+
if let Some(rivet_err) = err.chain().find_map(|x| x.downcast_ref::<RivetError>()) {
|
|
2566
|
+
let status = match (rivet_err.group(), rivet_err.code()) {
|
|
2567
|
+
("api", "not_found") => StatusCode::NOT_FOUND,
|
|
2568
|
+
("api", "unauthorized") => StatusCode::UNAUTHORIZED,
|
|
2569
|
+
("api", "forbidden") => StatusCode::FORBIDDEN,
|
|
2570
|
+
("guard", "rate_limit") => StatusCode::TOO_MANY_REQUESTS,
|
|
2571
|
+
("guard", "upstream_error") => StatusCode::BAD_GATEWAY,
|
|
2572
|
+
("guard", "routing_error") => StatusCode::BAD_GATEWAY,
|
|
2573
|
+
("guard", "request_timeout") => StatusCode::GATEWAY_TIMEOUT,
|
|
2574
|
+
("guard", "retry_attempts_exceeded") => StatusCode::BAD_GATEWAY,
|
|
2575
|
+
("guard", "actor_not_found") => StatusCode::NOT_FOUND,
|
|
2576
|
+
("guard", "actor_destroyed") => StatusCode::NOT_FOUND,
|
|
2577
|
+
("guard", "service_unavailable") => StatusCode::SERVICE_UNAVAILABLE,
|
|
2578
|
+
("guard", "actor_ready_timeout") => StatusCode::SERVICE_UNAVAILABLE,
|
|
2579
|
+
("guard", "no_route") => StatusCode::NOT_FOUND,
|
|
2580
|
+
_ => StatusCode::BAD_REQUEST,
|
|
2581
|
+
};
|
|
2582
|
+
|
|
2583
|
+
(status, ErrorResponse::from(rivet_err))
|
|
2584
|
+
} else if let Some(raw_err) = err
|
|
2585
|
+
.chain()
|
|
2586
|
+
.find_map(|x| x.downcast_ref::<RawErrorResponse>())
|
|
2587
|
+
{
|
|
2588
|
+
(raw_err.0, raw_err.1.clone())
|
|
2589
|
+
} else {
|
|
2590
|
+
(
|
|
2591
|
+
StatusCode::INTERNAL_SERVER_ERROR,
|
|
2592
|
+
ErrorResponse::from(&RivetError {
|
|
2593
|
+
schema: &rivet_error::INTERNAL_ERROR,
|
|
2594
|
+
meta: None,
|
|
2595
|
+
message: None,
|
|
2596
|
+
}),
|
|
2597
|
+
)
|
|
2598
|
+
};
|
|
2599
|
+
|
|
2600
|
+
let body_json = serde_json::to_vec(&error_response)?;
|
|
2601
|
+
let bytes = Bytes::from(body_json);
|
|
2602
|
+
|
|
2603
|
+
Response::builder()
|
|
2604
|
+
.status(status)
|
|
2605
|
+
.header(hyper::header::CONTENT_TYPE, "application/json")
|
|
2606
|
+
.body(ResponseBody::Full(Full::new(bytes)))
|
|
2607
|
+
.map_err(Into::into)
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
fn should_retry_request(res: &Result<Response<ResponseBody>>) -> bool {
|
|
2611
|
+
match res {
|
|
2612
|
+
Ok(resp) => should_retry_request_inner(resp.status(), resp.headers()),
|
|
2613
|
+
Err(err) => {
|
|
2614
|
+
if let Some(rivet_err) = err.chain().find_map(|x| x.downcast_ref::<RivetError>()) {
|
|
2615
|
+
rivet_err.group() == "guard" && rivet_err.code() == "service_unavailable"
|
|
2616
|
+
} else {
|
|
2617
|
+
false
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
// Determine if a response should trigger a retry: 503 + x-rivet-error
|
|
2624
|
+
fn should_retry_request_inner(status: StatusCode, headers: &hyper::HeaderMap) -> bool {
|
|
2625
|
+
status == StatusCode::SERVICE_UNAVAILABLE && headers.contains_key(X_RIVET_ERROR)
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
// Determine if a websocket error is retryable (e.g., transient UPS/tunnel issues)
|
|
2629
|
+
fn is_retryable_ws_error(err: &anyhow::Error) -> bool {
|
|
2630
|
+
if let Some(rivet_err) = err.chain().find_map(|x| x.downcast_ref::<RivetError>()) {
|
|
2631
|
+
rivet_err.group() == "guard" && rivet_err.code() == "websocket_service_unavailable"
|
|
2632
|
+
} else {
|
|
2633
|
+
false
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2637
|
+
pub fn is_ws_hibernate(err: &anyhow::Error) -> bool {
|
|
2638
|
+
if let Some(rivet_err) = err.chain().find_map(|x| x.downcast_ref::<RivetError>()) {
|
|
2639
|
+
rivet_err.group() == "guard" && rivet_err.code() == "websocket_service_hibernate"
|
|
2640
|
+
} else {
|
|
2641
|
+
false
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
fn str_to_close_frame(err: &str) -> CloseFrame {
|
|
2646
|
+
// NOTE: reason cannot be more than 123 bytes as per the WS protocol spec
|
|
2647
|
+
let reason = rivet_util::safe_slice(err, 0, 123).into();
|
|
2648
|
+
|
|
2649
|
+
CloseFrame {
|
|
2650
|
+
code: CloseCode::Error,
|
|
2651
|
+
reason,
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2655
|
+
fn err_to_close_frame(err: anyhow::Error, ray_id: Option<Id>) -> CloseFrame {
|
|
2656
|
+
let rivet_err = err
|
|
2657
|
+
.chain()
|
|
2658
|
+
.find_map(|x| x.downcast_ref::<RivetError>())
|
|
2659
|
+
.cloned()
|
|
2660
|
+
.unwrap_or_else(|| RivetError::from(&INTERNAL_ERROR));
|
|
2661
|
+
|
|
2662
|
+
let code = match (rivet_err.group(), rivet_err.code()) {
|
|
2663
|
+
("ws", "connection_closed") | ("ws", "eviction") => CloseCode::Normal,
|
|
2664
|
+
_ => CloseCode::Error,
|
|
2665
|
+
};
|
|
2666
|
+
|
|
2667
|
+
let reason = if let Some(ray_id) = ray_id {
|
|
2668
|
+
format!("{}.{}#{}", rivet_err.group(), rivet_err.code(), ray_id)
|
|
2669
|
+
} else {
|
|
2670
|
+
format!("{}.{}", rivet_err.group(), rivet_err.code())
|
|
2671
|
+
};
|
|
2672
|
+
|
|
2673
|
+
// NOTE: reason cannot be more than 123 bytes as per the WS protocol
|
|
2674
|
+
let reason = rivet_util::safe_slice(&reason, 0, 123).into();
|
|
2675
|
+
|
|
2676
|
+
CloseFrame { code, reason }
|
|
2677
|
+
}
|
|
2678
|
+
|
|
2679
|
+
fn to_hyper_close(frame: Option<CloseFrame>) -> hyper_tungstenite::tungstenite::Message {
|
|
2680
|
+
if let Some(frame) = frame {
|
|
2681
|
+
// Manual conversion to handle different tungstenite versions
|
|
2682
|
+
let code_num: u16 = frame.code.into();
|
|
2683
|
+
let reason = frame.reason.clone();
|
|
2684
|
+
|
|
2685
|
+
tokio_tungstenite::tungstenite::Message::Close(Some(
|
|
2686
|
+
tokio_tungstenite::tungstenite::protocol::CloseFrame {
|
|
2687
|
+
code: code_num.into(),
|
|
2688
|
+
reason,
|
|
2689
|
+
},
|
|
2690
|
+
))
|
|
2691
|
+
} else {
|
|
2692
|
+
tokio_tungstenite::tungstenite::Message::Close(Some(
|
|
2693
|
+
tokio_tungstenite::tungstenite::protocol::CloseFrame {
|
|
2694
|
+
code: CloseCode::Normal,
|
|
2695
|
+
reason: "ws.closed".into(),
|
|
2696
|
+
},
|
|
2697
|
+
))
|
|
2698
|
+
}
|
|
2699
|
+
}
|