Skip to content

AI translation

This page was translated from German with AI. If in doubt, the German original applies.

Client and connection

How to create the client, which transport it uses and how to set time limits per call.

Creating a client

ZedalClientFactory.Default offers overloads for three cases:

Overload When
CreateClient(TargetSystem, clientName, clientVersion, …) The standard case. The SDK derives the address from the environment.
CreateClient(string address, clientName, clientVersion, …) Explicit address; used as is and not rewritten to a gateway.
CreateClient(IZedalConnection, clientName, clientVersion, …) You built the connection yourself via ZedalConnectionFactory — for example with your own interceptors, or to create your own stubs via Channel / CallInvoker using the SDK’s conventions (proxy, message sizes).

All overloads take the same optional parameters:

Parameter Meaning
tokenRefresh Your own implementation of IJwtTokenRefresh instead of the default strategy (see Token renewal)
transport ZedalTransport; default Auto (see Transport)
configureHandler Action<HttpClientHandler>; applied to every handler the connection creates — probe and channel
proxyUrl SDK-specific proxy; takes precedence over the environment variable

clientName and clientVersion are sent as headers with every call and appear in the server-side logs. Use the values you provided during registration; this speeds up support requests.

TargetSystem is an enum with Production = 1, Preview = 2, Test = 3, Development = 4 and lives in the Zedal.Sdk.Impl namespace. Which hosts are behind it and why you develop against Test is described under Environments.

Transport

The SDK talks to ZEDAL either via native gRPC over HTTP/2 or via gRPC-Web over HTTP/1.1. The two paths are on different hosts: native gRPC on api.<environment>.zedal.de, gRPC-Web on the gateway openapi.<environment>.zedal.de. The SDK converts the address by replacing https://api. with https://openapi..

ZedalTransport Behavior
Auto (default) Checks once, when the connection is created, which HTTP version the native host negotiates, and decides accordingly.
Grpc Always native gRPC over HTTP/2 against api.<environment>.
GrpcWeb Always gRPC-Web against openapi.<environment>; outgoing requests are pinned to HTTP/1.1.
Unknown Sentinel for “not set”. Not a usable transport.

The rules of Auto in detail — they are deliberately conservative:

  • HTTP/2 available → Grpc.
  • The host responds, but only with HTTP/1.x → GrpcWeb, but only for a connection built via TargetSystem. This is the only case in which the SDK downgrades: positive evidence plus a known gateway.
  • No response (offline, timeout, DNS error) → it stays with Grpc. A missing network is no proof that HTTP/2 is blocked.
  • Explicit address → always stays with Grpc. The address is not rewritten, and an arbitrary host has no gRPC-Web counterpart.

client.Connection.Transport then returns the resolved value and is never Auto.

The auto probe is a blocking network call in the constructor

CreateClient(...) with transport: ZedalTransport.Auto sends an HTTP request before it returns (probe time limit: 5 seconds). In startup paths that must not block, and in environments where HTTP/2 is blocked anyway, set the transport directly:

var client = ZedalClientFactory.Default.CreateClient(
    system:        TargetSystem.Test,
    clientName:    "MyIntegrationApp",
    clientVersion: "1.0.0",
    transport:     ZedalTransport.GrpcWeb);

Proxy in corporate networks

A process-wide HTTP_PROXY affects all connections of the application and breaks off-domain scenarios (home office, VPN off). The SDK therefore comes with its own proxy, limited to ZEDAL traffic.

Option Effect
proxyUrl parameter takes precedence over everything else
INFOTECH_GRPC_PROXY_URL environment variable applies if no parameter is set
Value bypass (parameter or variable, case-insensitive) turns proxying off completely — including a configured system proxy
INFOTECH_GRPC_NO_PROXY environment variable deprecated; acts like bypass and overrides a configured proxy URL. Use the sentinel instead.

The proxy setting is parsed leniently: host:port is enough (gate:3128), and a missing scheme is completed with http. The auto probe uses the same route — so it checks the path the real calls take.

Checking the connection

When nothing works, CheckConnection answers the question “is it the network?” without a business call:

var status = await client.Connection.CheckConnection();

Console.WriteLine($"{status.Address} reachable={status.Reachable} " +
                  $"transport={status.Transport} http={status.HttpVersion} " +
                  $"proxy={status.ProxyAddress} rtt={status.RoundTripTime}");

ZedalConnectionStatus carries Reachable, Transport, Address, HttpVersion, RoundTripTime, Error, ProxyAddress, and UsesProxy. The test is purely transport-related: it says nothing about authentication and nothing about whether the required gRPC services are mapped on the endpoint. For a check at the service level, use client.DiscoveryApi.ListProvider() — the call doesn’t need a token.

Message size

The SDK raises the gRPC message limit for sending and receiving to 50 MB. The Grpc.Net.Client default (4 MB receive) is too small for document and attachment payloads.

Time limits: ZedalCallContext

Every API method of the SDK takes an optional ZedalCallContext as its last parameter. It bundles the deadline, a CancellationToken, and additional gRPC headers for one call.

Value Meaning
client.DefaultTimeout Client-wide default in seconds. Initial value 30.
ZedalCallContext.ClientDefaultTimeout (0) Sentinel for “caller has not set anything” → client.DefaultTimeout applies. Not a number of seconds and not “unlimited”.
ZedalCallContext.NoTimeout (-1) No deadline. Only useful together with a CancellationToken.

0 is not shorthand for “unlimited”

client.DefaultTimeout = 0 would create a deadline of “now” and make every call fail immediately with DeadlineExceeded. That is why both the setter and the context’s constructor reject 0 and all negative values other than NoTimeout with ArgumentOutOfRangeException. To turn the deadline off, use NoTimeout or WithoutDeadline().

The With… methods are instance withers: each returns a copy with exactly one field changed, and the order of a chain doesn’t matter.

Method Effect
WithTimeout(seconds) Deadline in seconds
WithoutDeadline() Turn off the deadline
WithCancellation(token) Set the CancellationToken
WithExtraHeaders(metadata) Replace extra headers
WithAdditionalHeaders(metadata) Add extra headers; for the same key, the existing entry wins
WithFallbackTimeout(seconds) Set the deadline only if the caller has not set one
// Generous, finite deadline plus cancellation option
await client.DiwassApi.AttachmentApi.GetAttachment(attachmentId,
    ZedalCallContext.Default.WithTimeout(600).WithCancellation(token));

// Only the token cancels
await client.DiwassApi.AttachmentApi.GetAttachment(attachmentId,
    ZedalCallContext.Default.WithoutDeadline().WithCancellation(token));

// A CancellationToken alone is converted implicitly
await client.CoreApi.Documents.ExportCurrentDocument(docId, context: token);

Deadline and cancellation remain distinguishable for the caller: an exceeded deadline comes back as RpcException with StatusCode.DeadlineExceeded, a cancellation via the token as OperationCanceledException. A token you pass does not remove the deadline; if you want to take over timing completely, say so with WithoutDeadline().

30 seconds are not enough for DIWASS paths

Calls that the server forwards to the EU system need a multiple of the default. Set the deadline per call instead of raising the client default globally — the budgets differ considerably from operation to operation. The measured guideline values are listed with the calls they belong to: Lifecycle → Sending procedure.