fix(core): route host-style storage addressing on emulator hostnames - #268
Conversation
Real Azure addresses storage host-style: the account is the first host
label and the container is the first path segment. AzureRoutingFilter
resolved the account only from the path — host-based routing existed
solely for the production suffixes like .blob.core.windows.net — so a
host-style request against an emulator hostname had its container
consumed as the account and Create Container answered 501. The .NET
Azure Functions host emits exactly that shape for AzureWebJobsStorage,
so its azure-webjobs-hosts bootstrap failed and Durable/timer triggers
never started.
Derive a service-marker table from the registered host suffixes' first
labels (blob, dfs, queue, table, vault, communication, servicebus) and
add a routing stage that resolves {account}.{marker}.{any-host} exactly
like {account}.{marker}.core.windows.net — devstoreaccount1.blob.localhost
and devstoreaccount1.blob.floci-az now route to blob with account
devstoreaccount1 and the full path as the resource. Path-style callers
are unaffected: hosts without a service marker as their second label
fall through to the account-suffix terminal unchanged, and duplicate
markers across service types fail fast at startup. TableServiceHandler
now registers .table.core.windows.net, so table gains both the
production suffix and the marker, completing the blob/queue/table trio
AzureWebJobsStorage needs.
Routing tests pin the issue reproduction (host-style Create Container),
host/path namespace sharing, queue and table marker hosts, the new
table suffix, and that a dotted marker-less host stays path-style.
Closes floci-io#267
|
| Filename | Overview |
|---|---|
| src/main/java/io/floci/az/core/AzureRoutingFilter.java | Adds case-insensitive host service-marker routing while preserving production-suffix priority and path-style fallback. |
| src/main/java/io/floci/az/services/table/TableServiceHandler.java | Registers the standard Table Storage host suffix so both production-style and emulator-marker routing reach the table handler. |
| src/test/java/io/floci/az/core/AzureRoutingFilterTest.java | Covers host-style Blob, Queue, and Table routing, mixed-case host normalization, shared account state, and marker-less FQDN fallback. |
| src/test/java/io/floci/az/core/RoutingTableAssemblyTest.java | Updates the routing-table golden set to require the newly registered Table Storage host suffix. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Incoming storage request] --> B[Normalize Host and remove port]
B --> C{Production host suffix matches?}
C -- Yes --> D[Extract account and dispatch service]
C -- No --> E{Second host label is registered service marker?}
E -- Yes --> F[Extract first label as account]
F --> D
E -- No --> G[Continue path-based routing]
G --> H[Account-suffix or storage fallback]
Reviews (2): Last reviewed commit: "fix(core): compare hostnames case-insens..." | Re-trigger Greptile
Hostnames are case-insensitive (RFC 4343), but both host-routing stages compared the raw Host header: the marker lookup missed devstoreaccount1.BLOB.localhost and fell back to path-style — recreating the bootstrap failure — and the pre-existing production-suffix stage equally missed acct.BLOB.core.windows.net. Lowercase the host once at capture (hostWithoutPort, alongside the port strip) so every host comparison sees the normalized form and the account label lands in the lowercase namespace Azure account names use. A mixed-case host-style create-container test pins the behavior, including visibility of the container in the lowercase path-style namespace.
hectorvent
left a comment
There was a problem hiding this comment.
Thank you, this is a clean fix and the reasoning in the description is the best part. I checked the design choice against Azurite, which treats any dotted hostname as production style with no marker required (blobStorageContext.middleware.ts#L271). Requiring a known service marker is stricter than that on purpose, and you are right to be: an emulator served at an FQDN would otherwise have its first label eaten as the account, which is exactly the footgun Azurite ships a flag to disable. The FQDN regression test pins that decision.
The case-insensitivity follow-up landed where I would have asked for it: normalising once in hostWithoutPort covers the production-suffix stage as well as the new marker stage, and the mixed-case test proves the account lands in the lowercase namespace rather than in one that matches nothing.
No blockers from my side.
Closes #267
Problem
Real Azure addresses storage host-style: the account is the first host label and the container is the first path segment.
AzureRoutingFilterresolved the account only from the path — host-based routing existed solely for the production suffixes (.blob.core.windows.net, …) — so a host-style request against an emulator hostname had its container consumed as the account and Create Container answered501 NotImplemented:The .NET Azure Functions host emits exactly this shape for
AzureWebJobsStorage, so theazure-webjobs-hostsbootstrap failed and Durable/timer triggers never started — the remaining blocker after #182/#183 (details and wire traces in #267).Fix
routeByHostServiceMarker(immediately after the production-suffix stage): a service-marker table is derived from the registered host suffixes' first labels (blob,dfs,queue,table,vault,communication,servicebus), and{account}.{marker}.{any-host}routes exactly like{account}.{marker}.core.windows.net. Sodevstoreaccount1.blob.localhostanddevstoreaccount1.blob.floci-azresolve to accountdevstoreaccount1, serviceblob, with the full path as the resource.TableServiceHandlernow registers.table.core.windows.net— table previously had no host route at all, so it gains both the production suffix and the marker, completing the blob/queue/table trioAzureWebJobsStorageneeds.floci-az.mycorp.local) falls through to the account-suffix terminal unchanged. Duplicate markers across service types fail fast at startup, same as the existing duplicate-suffix checks.With this, the host-style connection string works end to end (with docker network aliases or a wildcard DNS entry for the three names):
Host-style and path-style share the account namespace: a blob written through
devstoreaccount1.blob.…is readable at/devstoreaccount1/…and vice versa.Tests
New routing tests (all watched fail with the exact #267 symptom — 501 / misrouted list — before the fix):
.table.core.windows.netproduction-suffix routingHoststays path-style (regression guard for FQDN deployments)RoutingTableAssemblyTest's golden host table updated for the new table suffix. Full unit suite green.Docs follow-up: the README's connection-string section only documents path-style; happy to add the host-style shape in a separate docs PR if wanted.