spatialdds 0.5.2__tar.gz → 0.5.4__tar.gz
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.
- {spatialdds-0.5.2 → spatialdds-0.5.4}/PKG-INFO +46 -3
- {spatialdds-0.5.2 → spatialdds-0.5.4}/README.md +45 -2
- {spatialdds-0.5.2 → spatialdds-0.5.4}/pyproject.toml +1 -1
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/__init__.py +1 -1
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/server/gw_http.py +177 -22
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds.egg-info/PKG-INFO +46 -3
- {spatialdds-0.5.2 → spatialdds-0.5.4}/setup.cfg +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/idl/core.idl +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/idl/discovery.idl +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/idl/types.idl +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/builtin_Time.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_anchors_AnchorDelta.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_anchors_AnchorEntry.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_anchors_AnchorSet.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_anchors_AnchorSetRequest.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_anchors_AnchorSetResponse.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_argeo_NodeGeo.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_argeo_QualityRequirements.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_argeo_VpsRequest.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_argeo_VpsResponse.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_common_AssetRef.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_common_FrameRef.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_common_KV.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_common_MetaKV.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_common_Vec2.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_Aabb3.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_BlobChunk.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_BlobRef.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_ComponentRef.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_Edge.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_EntityBinding.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_FrameTransform.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_FramedPose.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_GeoAnchor.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_GeoPose.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_NavSatStatus.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_Node.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_PlannedTrajectory.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_PlannedWaypoint.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_PoseSE3.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_SnapshotRequest.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_SnapshotResponse.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_TileKey.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_TileMeta.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_TilePatch.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_Announce.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_Capabilities.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_ContentAnnounce.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_CoverageElement.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_CoverageFilter.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_CoverageHint.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_CoverageQuery.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_CoverageResponse.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_Depart.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_KV.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_ProfileSupport.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_ServiceSummary.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_TopicMeta.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_Transform.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_ValidityWindow.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_events_CrossingLine.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_events_SpatialEvent.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_events_SpatialZone.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_events_ZoneState.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_mapping_Edge.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_mapping_MapAlignment.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_mapping_MapEvent.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_mapping_MapMeta.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_mapping_MapQuality.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_owm_Entity.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_owm_ModelCommand.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_owm_ModelPose.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_semantics_Detection2D.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_semantics_Detection2DSet.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_semantics_Detection3D.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_semantics_Detection3DSet.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_semantics_FusedTrack.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_semantics_FusedTrackSet.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_semantics_Keypoint3D.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_semantics_KeypointLink.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_common_Axis.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_common_FrameHeader.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_common_FrameQuality.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_common_Linspace.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_common_ROI.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_common_ROIReply.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_common_ROIRequest.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_common_StreamMeta.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarDetection.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarDetectionSet.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarFrame.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_lidar_LidarMeta.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadDetection.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadDetectionSet.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadSensorMeta.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadTensorFrame.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_rad_RadTensorMeta.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_radio_RadioObservation.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_radio_RadioScan.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_radio_RadioSensorMeta.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_rf_beam_RfBeamArraySet.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_rf_beam_RfBeamFrame.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_rf_beam_RfBeamMeta.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_vision_CamIntrinsics.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_vision_Keypoint2D.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_vision_Track2D.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_vision_VisionDetections.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_vision_VisionFrame.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_vision_VisionMeta.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_slam_frontend_CameraInfo.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_slam_frontend_Feature2D.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_slam_frontend_FeatureMatch.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_slam_frontend_KeyframeFeatures.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_slam_frontend_Landmark.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_slam_frontend_MatchSet.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_slam_frontend_TrackObs.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_slam_frontend_Tracklet.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_vio_FusedState.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_vio_ImuInfo.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_vio_ImuSample.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_vio_MagnetometerSample.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_vio_SensorFusionSample.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/serializer_tables.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/canonical.py +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/canonjson.py +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/client.py +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/demo/assets/duck.gltf +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/demo/assets/fountain-label.txt +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/demo/assets/fountain-marker.gltf +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/demo/fountain-anchors.mcap +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/demo/manifests/content-fountain-label.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/demo/manifests/content-fountain-marker.json +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/errors.py +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/records.py +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/serve.py +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/server/__init__.py +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/server/commands.py +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/server/gateway.py +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/server/idlmodel.py +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/server/pin.py +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/server/publish.py +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/server/webjson.py +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/server/xcdr2.py +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds.egg-info/SOURCES.txt +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds.egg-info/dependency_links.txt +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds.egg-info/entry_points.txt +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds.egg-info/requires.txt +0 -0
- {spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spatialdds
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.4
|
|
4
4
|
Summary: SpatialDDS over plain HTTPS and a WebSocket: a client for the web binding (Appendix N), and the reference registry behind one command.
|
|
5
5
|
Author: Open AR Cloud
|
|
6
6
|
License: Apache-2.0
|
|
@@ -419,7 +419,7 @@ an object the app had already discarded. If you called `register()` before
|
|
|
419
419
|
`make_app()`, 0.4.0 and 0.5.0 worked; if you called it after, the command lane
|
|
420
420
|
advertised nothing and refused every POST.
|
|
421
421
|
|
|
422
|
-
0.5.2
|
|
422
|
+
0.5.2 made `notify` safe from the thread its own docstring invited. It put
|
|
423
423
|
onto the subscriber queue directly, and `asyncio.Queue.put_nowait` from a
|
|
424
424
|
foreign thread enqueues without waking the loop — so a backend reading a bus
|
|
425
425
|
in a thread fed a parked subscriber nothing until the loop woke for some other
|
|
@@ -428,7 +428,50 @@ pumping 202 samples into two registered subscriber queues delivered 0 live
|
|
|
428
428
|
events. If you call `notify` only from the serving loop, 0.5.0 and 0.5.1 were
|
|
429
429
|
correct for you.
|
|
430
430
|
|
|
431
|
-
|
|
431
|
+
0.5.3 let a browser reach this gateway at all. `Authorization` is not a
|
|
432
|
+
CORS-safelisted request header, so a cross-origin request carrying one is
|
|
433
|
+
preceded by an `OPTIONS` preflight — and no `OPTIONS` was routed, so aiohttp
|
|
434
|
+
answered `405 Allow: GET,HEAD,POST` and the browser stopped there without
|
|
435
|
+
making the request. Two consequences, both found from outside by a real page
|
|
436
|
+
rather than by a gate here: **N.6 was unreachable from a browser for every
|
|
437
|
+
access class**, because a command always carries a credential and so always
|
|
438
|
+
preflights, which no reclassification can change; and a `public-cacheable`
|
|
439
|
+
`GET` that answers `200` with `Access-Control-Allow-Origin: *` failed outright
|
|
440
|
+
if the client volunteered a credential it did not need.
|
|
441
|
+
|
|
442
|
+
An authorized operator-scoped response now also carries the asking origin,
|
|
443
|
+
echoed, with `Vary: Origin, Authorization`. The previous reasoning was that a
|
|
444
|
+
gated resource should never be CORS-open because its credential "must not be
|
|
445
|
+
invited to attach ambiently" — right about the risk and wrong about the
|
|
446
|
+
mechanism. `Access-Control-Allow-Credentials` is what issues that invitation,
|
|
447
|
+
this package sets it nowhere, and a `Bearer` token the page holds and chooses
|
|
448
|
+
to attach is the opposite of ambient. Refusing the origin never kept a
|
|
449
|
+
credential out; it made an authorized `200` unreadable by the client that had
|
|
450
|
+
authorized itself. The body is still `no-store`, the credential is still
|
|
451
|
+
required, and nothing is newly reachable without one.
|
|
452
|
+
|
|
453
|
+
If your clients are not browsers, 0.5.0 through 0.5.2 were correct for you:
|
|
454
|
+
nothing else changes, and no response loses a header it had.
|
|
455
|
+
|
|
456
|
+
0.5.4 finishes what 0.5.3 started, and the gap is instructive. 0.5.3 threaded
|
|
457
|
+
the asking origin through the three places that build a response from
|
|
458
|
+
`_cache_headers` and missed the two that build one by hand: the N.6 command
|
|
459
|
+
lane's own reply, and **every N.8 problem document**. So a command published
|
|
460
|
+
and the page could not read the outcome --- a cross-origin `fetch` rejects a
|
|
461
|
+
response with no allowed origin, so a client saw a failure for a command that
|
|
462
|
+
had in fact succeeded --- and a `401`, `409` or `400` was unreadable by the
|
|
463
|
+
client it was written for, which is N.8 failing at the one thing that
|
|
464
|
+
distinguishes it from a bare status.
|
|
465
|
+
|
|
466
|
+
The fix is a one-rule middleware rather than a fourth and fifth call site:
|
|
467
|
+
where a response names no origin and the request had one, echo it with
|
|
468
|
+
`Vary: Origin`. `_cache_headers` remains the place that decides cacheability
|
|
469
|
+
and reach together and is unchanged where it already acts. Threading it
|
|
470
|
+
explicitly was tried first and is what missed two paths.
|
|
471
|
+
|
|
472
|
+
If your clients are not browsers, nothing here changes anything for you.
|
|
473
|
+
|
|
474
|
+
## Not in 0.5.4
|
|
432
475
|
|
|
433
476
|
Stated so nobody waits for them: no async API, no caching beyond ETag
|
|
434
477
|
revalidation, no retry policy, no MCAP tooling beyond the journal the server
|
|
@@ -395,7 +395,7 @@ an object the app had already discarded. If you called `register()` before
|
|
|
395
395
|
`make_app()`, 0.4.0 and 0.5.0 worked; if you called it after, the command lane
|
|
396
396
|
advertised nothing and refused every POST.
|
|
397
397
|
|
|
398
|
-
0.5.2
|
|
398
|
+
0.5.2 made `notify` safe from the thread its own docstring invited. It put
|
|
399
399
|
onto the subscriber queue directly, and `asyncio.Queue.put_nowait` from a
|
|
400
400
|
foreign thread enqueues without waking the loop — so a backend reading a bus
|
|
401
401
|
in a thread fed a parked subscriber nothing until the loop woke for some other
|
|
@@ -404,7 +404,50 @@ pumping 202 samples into two registered subscriber queues delivered 0 live
|
|
|
404
404
|
events. If you call `notify` only from the serving loop, 0.5.0 and 0.5.1 were
|
|
405
405
|
correct for you.
|
|
406
406
|
|
|
407
|
-
|
|
407
|
+
0.5.3 let a browser reach this gateway at all. `Authorization` is not a
|
|
408
|
+
CORS-safelisted request header, so a cross-origin request carrying one is
|
|
409
|
+
preceded by an `OPTIONS` preflight — and no `OPTIONS` was routed, so aiohttp
|
|
410
|
+
answered `405 Allow: GET,HEAD,POST` and the browser stopped there without
|
|
411
|
+
making the request. Two consequences, both found from outside by a real page
|
|
412
|
+
rather than by a gate here: **N.6 was unreachable from a browser for every
|
|
413
|
+
access class**, because a command always carries a credential and so always
|
|
414
|
+
preflights, which no reclassification can change; and a `public-cacheable`
|
|
415
|
+
`GET` that answers `200` with `Access-Control-Allow-Origin: *` failed outright
|
|
416
|
+
if the client volunteered a credential it did not need.
|
|
417
|
+
|
|
418
|
+
An authorized operator-scoped response now also carries the asking origin,
|
|
419
|
+
echoed, with `Vary: Origin, Authorization`. The previous reasoning was that a
|
|
420
|
+
gated resource should never be CORS-open because its credential "must not be
|
|
421
|
+
invited to attach ambiently" — right about the risk and wrong about the
|
|
422
|
+
mechanism. `Access-Control-Allow-Credentials` is what issues that invitation,
|
|
423
|
+
this package sets it nowhere, and a `Bearer` token the page holds and chooses
|
|
424
|
+
to attach is the opposite of ambient. Refusing the origin never kept a
|
|
425
|
+
credential out; it made an authorized `200` unreadable by the client that had
|
|
426
|
+
authorized itself. The body is still `no-store`, the credential is still
|
|
427
|
+
required, and nothing is newly reachable without one.
|
|
428
|
+
|
|
429
|
+
If your clients are not browsers, 0.5.0 through 0.5.2 were correct for you:
|
|
430
|
+
nothing else changes, and no response loses a header it had.
|
|
431
|
+
|
|
432
|
+
0.5.4 finishes what 0.5.3 started, and the gap is instructive. 0.5.3 threaded
|
|
433
|
+
the asking origin through the three places that build a response from
|
|
434
|
+
`_cache_headers` and missed the two that build one by hand: the N.6 command
|
|
435
|
+
lane's own reply, and **every N.8 problem document**. So a command published
|
|
436
|
+
and the page could not read the outcome --- a cross-origin `fetch` rejects a
|
|
437
|
+
response with no allowed origin, so a client saw a failure for a command that
|
|
438
|
+
had in fact succeeded --- and a `401`, `409` or `400` was unreadable by the
|
|
439
|
+
client it was written for, which is N.8 failing at the one thing that
|
|
440
|
+
distinguishes it from a bare status.
|
|
441
|
+
|
|
442
|
+
The fix is a one-rule middleware rather than a fourth and fifth call site:
|
|
443
|
+
where a response names no origin and the request had one, echo it with
|
|
444
|
+
`Vary: Origin`. `_cache_headers` remains the place that decides cacheability
|
|
445
|
+
and reach together and is unchanged where it already acts. Threading it
|
|
446
|
+
explicitly was tried first and is what missed two paths.
|
|
447
|
+
|
|
448
|
+
If your clients are not browsers, nothing here changes anything for you.
|
|
449
|
+
|
|
450
|
+
## Not in 0.5.4
|
|
408
451
|
|
|
409
452
|
Stated so nobody waits for them: no async API, no caching beyond ETag
|
|
410
453
|
revalidation, no retry policy, no MCAP tooling beyond the journal the server
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "spatialdds"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.4"
|
|
8
8
|
description = "SpatialDDS over plain HTTPS and a WebSocket: a client for the web binding (Appendix N), and the reference registry behind one command."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -37,7 +37,7 @@ __all__ = [
|
|
|
37
37
|
"content_manifest", "geo_anchor", "service_uri", "content_uri",
|
|
38
38
|
]
|
|
39
39
|
|
|
40
|
-
__version__ = "0.5.
|
|
40
|
+
__version__ = "0.5.4"
|
|
41
41
|
#: The SpatialDDS-spec commit whose Appendix N and generated artifacts this
|
|
42
42
|
#: package implements. Package versions and specification versions move
|
|
43
43
|
#: independently; this is the one that says what the wire looks like.
|
|
@@ -91,20 +91,55 @@ def _problem(status: int, slug: str, title: str, detail: str,
|
|
|
91
91
|
headers={"Cache-Control": "no-store"})
|
|
92
92
|
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
#: What a cross-origin reader may see beyond the body. Without it the ETag and
|
|
95
|
+
#: the access class are served and unreadable, which is the instrument failing
|
|
96
|
+
#: to report what it measures.
|
|
97
|
+
EXPOSE_HEADERS = ("ETag, X-SpatialDDS-Topic, X-SpatialDDS-Type, "
|
|
98
|
+
"X-SpatialDDS-Access")
|
|
99
|
+
|
|
100
|
+
#: Request headers a browser may send to this gateway. `Authorization` is the
|
|
101
|
+
#: one that matters and the one that is not CORS-safelisted, so a request
|
|
102
|
+
#: carrying it preflights; `Content-Type` is needed for any JSON body, and
|
|
103
|
+
#: `If-None-Match` for conditional GETs.
|
|
104
|
+
ALLOW_HEADERS = "Authorization, Content-Type, If-None-Match"
|
|
105
|
+
|
|
106
|
+
#: Methods this gateway serves. Stated once so the preflight answer and the
|
|
107
|
+
#: `Allow` header on a refusal cannot drift apart.
|
|
108
|
+
ALLOW_METHODS = "GET, HEAD, POST, PUT, DELETE, OPTIONS"
|
|
109
|
+
|
|
110
|
+
#: How long a browser may cache a preflight. Ten minutes: long enough that a
|
|
111
|
+
#: page does not preflight every command, short enough that changing the
|
|
112
|
+
#: policy takes effect inside a session.
|
|
113
|
+
PREFLIGHT_MAX_AGE = 600
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _cache_headers(access: str, doc_ttl: int | None = None,
|
|
117
|
+
origin: str | None = None) -> dict[str, str]:
|
|
95
118
|
"""Cacheability, reach, and auth decided together or not at all.
|
|
96
119
|
|
|
97
120
|
The CORS header lives in this branch rather than in a middleware for the
|
|
98
121
|
same reason the cache header does: a public-cacheable resource is one a
|
|
99
122
|
client may read without presenting anything, so there is no origin that
|
|
100
|
-
should be refused it
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
123
|
+
should be refused it. Putting the two in one function makes the pairing
|
|
124
|
+
impossible to drift apart.
|
|
125
|
+
|
|
126
|
+
**An operator-scoped response now carries an echoed origin too, and the
|
|
127
|
+
reasoning that said otherwise was close to right.** It read: gated by "a
|
|
128
|
+
credential a browser must not be invited to attach ambiently". The
|
|
129
|
+
invitation is real and `Access-Control-Allow-Origin` is not how it is
|
|
130
|
+
issued --- `Access-Control-Allow-Credentials` is. Cookies and HTTP auth
|
|
131
|
+
ride along only when that header says so and the client asks for
|
|
132
|
+
`credentials: 'include'`; this gateway sets it nowhere and never will, so
|
|
133
|
+
nothing here is ambient. A `Bearer` token in an `Authorization` header is
|
|
134
|
+
the opposite of ambient: the page had to hold it and choose to attach it.
|
|
135
|
+
|
|
136
|
+
Refusing the origin did not keep that credential out. It made an
|
|
137
|
+
authorized response unreadable by the client that authorized itself ---
|
|
138
|
+
measured from `SpatialDDS-demo`'s browser lane, where a page holding a
|
|
139
|
+
valid token could not read a word of a 200 it had earned. So the origin is
|
|
140
|
+
echoed rather than `*`, with `Vary: Origin`, which keeps a shared cache
|
|
141
|
+
from serving one origin's answer to another; the body stays `no-store`
|
|
142
|
+
regardless, and the credential is still required. No gate is weakened.
|
|
108
143
|
"""
|
|
109
144
|
if access == gateway.PUBLIC:
|
|
110
145
|
# N.4: where a public-cacheable resource is a manifest carrying its own
|
|
@@ -113,15 +148,116 @@ def _cache_headers(access: str, doc_ttl: int | None = None) -> dict[str, str]:
|
|
|
113
148
|
age = gateway.PUBLIC_MAX_AGE if doc_ttl is None else doc_ttl
|
|
114
149
|
return {"Cache-Control": f"public, max-age={age}",
|
|
115
150
|
"Access-Control-Allow-Origin": "*",
|
|
116
|
-
"Access-Control-Expose-Headers":
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
#
|
|
120
|
-
#
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
151
|
+
"Access-Control-Expose-Headers": EXPOSE_HEADERS}
|
|
152
|
+
# Operator-scoped. Never cacheable, by the same decision that gated it.
|
|
153
|
+
# `doc_ttl` is ignored here deliberately: N.4 grants no lifetime to a gated
|
|
154
|
+
# resource whatever `ttl_sec` it carries, because the cache that would
|
|
155
|
+
# honour it is the shared one the gate exists to keep the body out of.
|
|
156
|
+
headers = {"Cache-Control": "no-store", "Vary": "Authorization"}
|
|
157
|
+
if origin:
|
|
158
|
+
# Echoed, not `*`: a wildcard on a response that varies by credential
|
|
159
|
+
# is the combination a shared cache can get wrong, and there is no
|
|
160
|
+
# reason to name an origin this request did not come from.
|
|
161
|
+
headers["Access-Control-Allow-Origin"] = origin
|
|
162
|
+
headers["Vary"] = "Origin, Authorization"
|
|
163
|
+
headers["Access-Control-Expose-Headers"] = EXPOSE_HEADERS
|
|
164
|
+
return headers
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@web.middleware
|
|
168
|
+
async def _reach(request: web.Request, handler) -> web.StreamResponse:
|
|
169
|
+
"""Make sure every answer names the origin that asked for it.
|
|
170
|
+
|
|
171
|
+
**A backstop, not the policy.** `_cache_headers` remains the single place
|
|
172
|
+
that decides cacheability and reach together, and where it has already set
|
|
173
|
+
`Access-Control-Allow-Origin` this changes nothing. What it covers is the
|
|
174
|
+
responses that set no reach at all, because they are built by hand rather
|
|
175
|
+
than through that function --- and the reason it exists is that threading
|
|
176
|
+
the origin explicitly through every construction site was tried first, in
|
|
177
|
+
0.5.3, and missed two:
|
|
178
|
+
|
|
179
|
+
* `post_command`'s `202`. The command published and the page could not
|
|
180
|
+
read the outcome: a cross-origin `fetch` rejects a response with no
|
|
181
|
+
allowed origin, so `bindingCommand` in the demo's client landed in its
|
|
182
|
+
`catch` with no status at all, for a command that had in fact succeeded.
|
|
183
|
+
* **Every N.8 problem document.** `_problem` set `no-store` and nothing
|
|
184
|
+
else, so a `401`, `409` or `400` was unreadable by the client it was
|
|
185
|
+
written for. N.8 exists to hand a client a *reason*; a reason a browser
|
|
186
|
+
cannot read is the surface failing at the one thing that distinguishes
|
|
187
|
+
it from a bare status.
|
|
188
|
+
|
|
189
|
+
Both were found by curling the running demo after 0.5.3 shipped, not by
|
|
190
|
+
this repository's gates --- the same way the original defect was found,
|
|
191
|
+
which is the lesson repeating rather than a new one.
|
|
192
|
+
|
|
193
|
+
Only the origin is echoed here, and only when absent. `Vary: Origin` goes
|
|
194
|
+
with it so a shared cache cannot serve one origin's answer to another; the
|
|
195
|
+
`Expose-Headers` set is added for the same reason it is on a GET, since a
|
|
196
|
+
reader that cannot see the ETag cannot revalidate. `Allow-Credentials` is
|
|
197
|
+
still set nowhere.
|
|
198
|
+
"""
|
|
199
|
+
response = await handler(request)
|
|
200
|
+
origin = request.headers.get("Origin")
|
|
201
|
+
if origin and "Access-Control-Allow-Origin" not in response.headers:
|
|
202
|
+
response.headers["Access-Control-Allow-Origin"] = origin
|
|
203
|
+
vary = response.headers.get("Vary")
|
|
204
|
+
response.headers["Vary"] = f"{vary}, Origin" if vary else "Origin"
|
|
205
|
+
response.headers.setdefault("Access-Control-Expose-Headers",
|
|
206
|
+
EXPOSE_HEADERS)
|
|
207
|
+
return response
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
async def preflight(request: web.Request) -> web.Response:
|
|
211
|
+
"""Answer the `OPTIONS` a browser sends before a credentialed request.
|
|
212
|
+
|
|
213
|
+
**Why this route has to exist, and why nothing in the specification said
|
|
214
|
+
so.** `Authorization` is not a CORS-safelisted request header, so a
|
|
215
|
+
cross-origin request carrying one is preceded by a preflight. This gateway
|
|
216
|
+
routed no `OPTIONS` at all, so aiohttp answered `405` with
|
|
217
|
+
`Allow: GET,HEAD,POST` --- and the browser stopped there, never making the
|
|
218
|
+
request. Two consequences, both measured against 0.5.2 from a page on
|
|
219
|
+
another origin:
|
|
220
|
+
|
|
221
|
+
* N.6 was unreachable from a browser for **every** access class, because a
|
|
222
|
+
command always carries a credential. Reclassifying a lane cannot help:
|
|
223
|
+
the preflight is triggered by the request, not by what the resource is.
|
|
224
|
+
* A `public-cacheable` GET that answers `200` with
|
|
225
|
+
`Access-Control-Allow-Origin: *` failed outright if the client
|
|
226
|
+
volunteered a credential it did not need.
|
|
227
|
+
|
|
228
|
+
Both are fixed here rather than in Appendix N, because nothing in N
|
|
229
|
+
forbade any of it --- a conforming endpoint could simply omit it, which is
|
|
230
|
+
the separate gap filed for the specification: a binding whose flagship
|
|
231
|
+
client is a browser should have to *state* its preflight behaviour.
|
|
232
|
+
|
|
233
|
+
`Allow-Credentials` is deliberately absent. It is the header that would
|
|
234
|
+
invite cookies and HTTP auth to ride along ambiently, and this gateway
|
|
235
|
+
wants the opposite: a credential the page holds and chooses to attach.
|
|
236
|
+
|
|
237
|
+
A bare `OPTIONS` with no `Origin` is not a preflight; it gets the same
|
|
238
|
+
`Allow` list as a courtesy, which also keeps `ALLOW_METHODS` the single
|
|
239
|
+
place that answer is written.
|
|
240
|
+
"""
|
|
241
|
+
origin = request.headers.get("Origin")
|
|
242
|
+
headers = {"Allow": ALLOW_METHODS}
|
|
243
|
+
if origin:
|
|
244
|
+
headers.update({
|
|
245
|
+
"Access-Control-Allow-Origin": origin,
|
|
246
|
+
"Access-Control-Allow-Methods": ALLOW_METHODS,
|
|
247
|
+
# Echo what was asked for when it was asked, so a header this
|
|
248
|
+
# gateway has not thought of is refused rather than blanket-allowed,
|
|
249
|
+
# and fall back to the list above for a preflight that names none.
|
|
250
|
+
"Access-Control-Allow-Headers":
|
|
251
|
+
request.headers.get("Access-Control-Request-Headers")
|
|
252
|
+
or ALLOW_HEADERS,
|
|
253
|
+
"Access-Control-Max-Age": str(PREFLIGHT_MAX_AGE),
|
|
254
|
+
"Access-Control-Expose-Headers": EXPOSE_HEADERS,
|
|
255
|
+
# The answer depends on all three, so a cache must not reuse it
|
|
256
|
+
# across different ones.
|
|
257
|
+
"Vary": "Origin, Access-Control-Request-Method, "
|
|
258
|
+
"Access-Control-Request-Headers",
|
|
259
|
+
})
|
|
260
|
+
return web.Response(status=204, headers=headers)
|
|
125
261
|
|
|
126
262
|
|
|
127
263
|
def _authorized(request: web.Request, token: str | None) -> bool:
|
|
@@ -145,7 +281,9 @@ def _json_response(request: web.Request, doc: Any, access: str,
|
|
|
145
281
|
doc_ttl: int | None = None) -> web.Response:
|
|
146
282
|
"""One JSON resource, with its ETag, its cache class, and 304 handling."""
|
|
147
283
|
etag = _etag(doc)
|
|
148
|
-
headers = {"ETag": etag,
|
|
284
|
+
headers = {"ETag": etag,
|
|
285
|
+
**_cache_headers(access, doc_ttl,
|
|
286
|
+
request.headers.get("Origin")),
|
|
149
287
|
**(extra or {})}
|
|
150
288
|
if request.headers.get("If-None-Match", "") == etag:
|
|
151
289
|
return web.Response(status=304, headers=headers)
|
|
@@ -473,7 +611,9 @@ def _written(request: web.Request, doc: Any, created: bool,
|
|
|
473
611
|
if not created else web.Response(
|
|
474
612
|
body=_body(doc), status=201, content_type="application/json",
|
|
475
613
|
headers={"ETag": _etag(doc),
|
|
476
|
-
**_cache_headers(gateway.OPERATOR
|
|
614
|
+
**_cache_headers(gateway.OPERATOR, None,
|
|
615
|
+
request.headers.get("Origin")),
|
|
616
|
+
**extra})
|
|
477
617
|
|
|
478
618
|
|
|
479
619
|
def _running_loop():
|
|
@@ -741,7 +881,9 @@ async def delete_instance(request: web.Request) -> web.Response:
|
|
|
741
881
|
{"event": "dispose", "topic": topic, "key": key,
|
|
742
882
|
"reason": reason})
|
|
743
883
|
return web.Response(status=204,
|
|
744
|
-
headers=_cache_headers(
|
|
884
|
+
headers=_cache_headers(
|
|
885
|
+
gateway.OPERATOR, None,
|
|
886
|
+
request.headers.get("Origin")))
|
|
745
887
|
|
|
746
888
|
|
|
747
889
|
async def put_manifest(request: web.Request) -> web.Response:
|
|
@@ -902,7 +1044,7 @@ def make_app(state: gateway.State, base: str, token: str | None,
|
|
|
902
1044
|
embedder passes `publish_enabled=False, live_subscriptions=True` and gets
|
|
903
1045
|
the live lane with no write surface at all.
|
|
904
1046
|
"""
|
|
905
|
-
app = web.Application()
|
|
1047
|
+
app = web.Application(middlewares=[_reach])
|
|
906
1048
|
app["state"] = state
|
|
907
1049
|
app["base"] = base.rstrip("/")
|
|
908
1050
|
app["token"] = token
|
|
@@ -959,5 +1101,18 @@ def make_app(state: gateway.State, base: str, token: str | None,
|
|
|
959
1101
|
web.delete("/{scene}/{stream}/{type}/{version}/{key}",
|
|
960
1102
|
delete_instance),
|
|
961
1103
|
web.put(gateway.RESOLVE_PATH, put_manifest),
|
|
1104
|
+
# The preflight, on every path that can be asked for cross-origin
|
|
1105
|
+
# with a credential or a JSON body. Mirrors the routes above rather
|
|
1106
|
+
# than using a catch-all, so a path this gateway does not serve still
|
|
1107
|
+
# 404s on OPTIONS instead of promising a method nobody implements.
|
|
1108
|
+
web.options(f"{WELL_KNOWN}/schemas", preflight),
|
|
1109
|
+
web.options(f"{WELL_KNOWN}/schemas/{{type}}", preflight),
|
|
1110
|
+
web.options(f"{WELL_KNOWN}/search", preflight),
|
|
1111
|
+
web.options(f"{WELL_KNOWN}/bootstrap", preflight),
|
|
1112
|
+
web.options(f"{WELL_KNOWN}/resolver", preflight),
|
|
1113
|
+
web.options(gateway.RESOLVE_PATH, preflight),
|
|
1114
|
+
web.options(f"{ASSET_PATH}/{{name}}", preflight),
|
|
1115
|
+
web.options("/{scene}/{stream}/{type}/{version}", preflight),
|
|
1116
|
+
web.options("/{scene}/{stream}/{type}/{version}/{key}", preflight),
|
|
962
1117
|
])
|
|
963
1118
|
return app
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spatialdds
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.4
|
|
4
4
|
Summary: SpatialDDS over plain HTTPS and a WebSocket: a client for the web binding (Appendix N), and the reference registry behind one command.
|
|
5
5
|
Author: Open AR Cloud
|
|
6
6
|
License: Apache-2.0
|
|
@@ -419,7 +419,7 @@ an object the app had already discarded. If you called `register()` before
|
|
|
419
419
|
`make_app()`, 0.4.0 and 0.5.0 worked; if you called it after, the command lane
|
|
420
420
|
advertised nothing and refused every POST.
|
|
421
421
|
|
|
422
|
-
0.5.2
|
|
422
|
+
0.5.2 made `notify` safe from the thread its own docstring invited. It put
|
|
423
423
|
onto the subscriber queue directly, and `asyncio.Queue.put_nowait` from a
|
|
424
424
|
foreign thread enqueues without waking the loop — so a backend reading a bus
|
|
425
425
|
in a thread fed a parked subscriber nothing until the loop woke for some other
|
|
@@ -428,7 +428,50 @@ pumping 202 samples into two registered subscriber queues delivered 0 live
|
|
|
428
428
|
events. If you call `notify` only from the serving loop, 0.5.0 and 0.5.1 were
|
|
429
429
|
correct for you.
|
|
430
430
|
|
|
431
|
-
|
|
431
|
+
0.5.3 let a browser reach this gateway at all. `Authorization` is not a
|
|
432
|
+
CORS-safelisted request header, so a cross-origin request carrying one is
|
|
433
|
+
preceded by an `OPTIONS` preflight — and no `OPTIONS` was routed, so aiohttp
|
|
434
|
+
answered `405 Allow: GET,HEAD,POST` and the browser stopped there without
|
|
435
|
+
making the request. Two consequences, both found from outside by a real page
|
|
436
|
+
rather than by a gate here: **N.6 was unreachable from a browser for every
|
|
437
|
+
access class**, because a command always carries a credential and so always
|
|
438
|
+
preflights, which no reclassification can change; and a `public-cacheable`
|
|
439
|
+
`GET` that answers `200` with `Access-Control-Allow-Origin: *` failed outright
|
|
440
|
+
if the client volunteered a credential it did not need.
|
|
441
|
+
|
|
442
|
+
An authorized operator-scoped response now also carries the asking origin,
|
|
443
|
+
echoed, with `Vary: Origin, Authorization`. The previous reasoning was that a
|
|
444
|
+
gated resource should never be CORS-open because its credential "must not be
|
|
445
|
+
invited to attach ambiently" — right about the risk and wrong about the
|
|
446
|
+
mechanism. `Access-Control-Allow-Credentials` is what issues that invitation,
|
|
447
|
+
this package sets it nowhere, and a `Bearer` token the page holds and chooses
|
|
448
|
+
to attach is the opposite of ambient. Refusing the origin never kept a
|
|
449
|
+
credential out; it made an authorized `200` unreadable by the client that had
|
|
450
|
+
authorized itself. The body is still `no-store`, the credential is still
|
|
451
|
+
required, and nothing is newly reachable without one.
|
|
452
|
+
|
|
453
|
+
If your clients are not browsers, 0.5.0 through 0.5.2 were correct for you:
|
|
454
|
+
nothing else changes, and no response loses a header it had.
|
|
455
|
+
|
|
456
|
+
0.5.4 finishes what 0.5.3 started, and the gap is instructive. 0.5.3 threaded
|
|
457
|
+
the asking origin through the three places that build a response from
|
|
458
|
+
`_cache_headers` and missed the two that build one by hand: the N.6 command
|
|
459
|
+
lane's own reply, and **every N.8 problem document**. So a command published
|
|
460
|
+
and the page could not read the outcome --- a cross-origin `fetch` rejects a
|
|
461
|
+
response with no allowed origin, so a client saw a failure for a command that
|
|
462
|
+
had in fact succeeded --- and a `401`, `409` or `400` was unreadable by the
|
|
463
|
+
client it was written for, which is N.8 failing at the one thing that
|
|
464
|
+
distinguishes it from a bare status.
|
|
465
|
+
|
|
466
|
+
The fix is a one-rule middleware rather than a fourth and fifth call site:
|
|
467
|
+
where a response names no origin and the request had one, echo it with
|
|
468
|
+
`Vary: Origin`. `_cache_headers` remains the place that decides cacheability
|
|
469
|
+
and reach together and is unchanged where it already acts. Threading it
|
|
470
|
+
explicitly was tried first and is what missed two paths.
|
|
471
|
+
|
|
472
|
+
If your clients are not browsers, nothing here changes anything for you.
|
|
473
|
+
|
|
474
|
+
## Not in 0.5.4
|
|
432
475
|
|
|
433
476
|
Stated so nobody waits for them: no async API, no caching beyond ETag
|
|
434
477
|
revalidation, no retry policy, no MCAP tooling beyond the journal the server
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_anchors_AnchorDelta.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_anchors_AnchorEntry.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_anchors_AnchorSet.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_argeo_NodeGeo.json
RENAMED
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_argeo_VpsRequest.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_argeo_VpsResponse.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_common_AssetRef.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_common_FrameRef.json
RENAMED
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_common_MetaKV.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_BlobChunk.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_BlobRef.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_ComponentRef.json
RENAMED
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_EntityBinding.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_FrameTransform.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_FramedPose.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_GeoAnchor.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_GeoPose.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_NavSatStatus.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_PlannedWaypoint.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_PoseSE3.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_SnapshotRequest.json
RENAMED
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_TileKey.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_TileMeta.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_core_TilePatch.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_Announce.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_Capabilities.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_CoverageFilter.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_CoverageHint.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_CoverageQuery.json
RENAMED
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_Depart.json
RENAMED
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_ProfileSupport.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_ServiceSummary.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_TopicMeta.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_Transform.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_disco_ValidityWindow.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_events_CrossingLine.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_events_SpatialEvent.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_events_SpatialZone.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_events_ZoneState.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_mapping_Edge.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_mapping_MapAlignment.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_mapping_MapEvent.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_mapping_MapMeta.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_mapping_MapQuality.json
RENAMED
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_owm_ModelCommand.json
RENAMED
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_owm_ModelPose.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_semantics_FusedTrack.json
RENAMED
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_semantics_Keypoint3D.json
RENAMED
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_common_Axis.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_sensing_common_ROI.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_vio_FusedState.json
RENAMED
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/artifacts/schemas/spatial_vio_ImuSample.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spatialdds-0.5.2 → spatialdds-0.5.4}/spatialdds/demo/manifests/content-fountain-marker.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|