- All Implemented Interfaces:
Job
This class is where we make several important decisions about what to send and what path to send it over. These decisions will dramatically affect:
- Local performance and outbound bandwidth usage
- Streaming performance and reliability
- Overall network performance and connection congestion
For the outbound message, we build and encrypt a garlic message, after making the following decisions:
- Whether to bundle our leaseset
- Whether to bundle session tags, and if so, how many
- Whether to bundle an encrypted DeliveryStatusMessage to be returned to us as an acknowledgement
Also, we make the following path selection decisions:
- What outbound client tunnel of ours to use send the message out
- What inbound client tunnel of his (i.e. lease, chosen from his leaseset) to use to send the message in
- If a DeliveryStatusMessage is bundled, What inbound client tunnel of ours do we specify to receive it
Note that the 4th tunnel in the DeliveryStatusMessage's round trip (his outbound tunnel) is not selected by us, it is chosen by the recipient.
If a DeliveryStatusMessage is sent, a listener is registered to wait for its reply. When a reply is received, or the timeout is reached, this is noted and will influence subsequent bundling and path selection decisions.
Path selection decisions are cached and reused if still valid and if previous deliveries were apparently successful. This significantly reduces out-of-order delivery and network connection congestion. Caching is based on the local/remote destination pair.
Bundling decisions, and both messaging and reply expiration times, are generally set here but may be overridden by the client on a per-message basis. Within clients, there may be overall settings or per-message settings. The streaming lib also overrides defaults for some messages. A datagram-based DHT application may need significantly different settings than a streaming application. For an application such as a bittorrent client that sends both types of traffic on the same tunnels, it is important to tune the settings for efficiency and performance. The per-session and per-message overrides are set via I2CP.
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprivate classNote: This is run inline bysend(), not on the job queue.private classFor ECIES only.private classWe couldn't even find the leaseSet, so die.private static classKeep an eye out for any of the delivery status message tokens that have been sent down the various tunnels to deliver this message.private static enumOutcome of one send attempt.private classSend a message to a lease.private classCalled after we get a confirmation that the message was delivered safely.private classFired after the basic timeout for sending through the given tunnel has been reached.Field Summary
Modifier and TypeFieldDescriptionprivate final OutboundCacheCache consulted for the target's lease set.private final ClientMessageThe message being sent.private final MessageIdId of the message being sent.private final intSize of the message in bytes.private PublicKeyActual target encryption key from the LS being used.The result reported when the job finishes.private final DestinationThe sending client's destination.private final OutboundCache.HashPairKey used to cache things with, based on source + dest.private TunnelInfoThe inbound tunnel the reply arrived on.private LeaseThe actual lease the message is being routed through.private LeaseSetThe target's lease set, if known.private longWhen the lease set lookup started.private final LogLogger for this send attempt.private TunnelInfoThe outbound tunnel the message was sent on.private final longWhen this send attempt gives up entirely.private final longWhen the job started.private final DestinationThe target destination.private final StringBase32 prefix of the target, for the logs.private booleanWhether the client asked for an ack.static final StringNOTE: Changed as of 0.9.2.private static final longTimeout for the lease set lookup.private static final intHow many pool picks to sample when gathering rotation candidates for a fresh connection.private static final longMinimum time in ms to keep using the same outbound tunnel for a given source-destination pair, to prevent rapid tunnel switching that causes out-of-order delivery and unnecessary NACKs in the streaming layer.private static final longDefault overall send timeout, one minute.private static final longLongest overall send timeout allowed.private static final longShortest overall send timeout allowed.static final StringFinal timeout (in milliseconds) that the outbound message will fail in.private static final longMinimum overall timeout when a lookup is needed.private static final longLeast time left for the ratchet reply callback, longer than the send timeout so a success after a failure still counts.private static final long[]Rate stat period for the send statistics.private static final intInterval between cached reply requests.private static final longMinimum time left for the reply after sending.private static final longDefault cooldown a stall mark stays in effect: how long the router prefers another outbound tunnel for a destination that stopped delivering.Constructor Summary
ConstructorsConstructorDescriptionOutboundClientMessageOneShotJob(RouterContext ctx, OutboundCache cache, ClientMessage msg) Send it.Method Summary
Modifier and TypeMethodDescriptionprivate PayloadGarlicConfigBuild the payload clove that will be used for all of the messages, placing the clove in the status structure.private voidCalled on failure to give us a better chance of success next time.private voiddieFatal(int status) Give up the ghost, this message just aint going through.private StringgetDestNickname(Hash dest) Resolve a human-readable nickname for a destination, if available.getName()Descriptive name of the task.private intChoose a lease from his leaseset to send the message to.private LeaseSetgetReplyLeaseSet(boolean force) Reply lease set, checking the cache first.static voidinit(RouterContext ctx) Called once at router startup.private booleanWhether this router hosts the given destination.(package private) static booleanisPoolStarvationFailure(int status) Whether the failure status means the destination's pools were empty or starved when the send needed them: 14 (expired while queued — nothing carried it before the deadline) and 16 (no tunnels available).(package private) static booleanisSoftSendFailure(int status) Whether the status is a best-effort send timeout (3) after a successful dispatch.private booleanWhether the current (source, destination) pair is inside a stall cooldown, and rotation is enabled.(package private) static booleanisTunnelRelatedFailure(int status) Whether the I2CP failure status indicates the outbound tunnel itself rejected the message during dispatch, as opposed to a timeout, destination problem, or reply-path issue.voidrunJob()Actually perform the task.private TunnelInfoTry to pick a different outbound tunnel than the one the previous connection to this destination used.private TunnelInfoPick an arbitrary inbound tunnel for any deliveryStatusMessage to come back in.private TunnelInfoPick an arbitrary outbound tunnel to send the message through, or null if there aren't any aroundprivate TunnelInfoChoose our outbound tunnel to send the message through.private voidsend()Send the message to the specified tunnel by creating a new garlic message containing the (already created) payload clove as well as a new delivery status message.(package private) static booleanshouldBlameOutboundTunnel(int status, boolean locallyHosted) Whether a failed send should be charged to the outbound tunnel.(package private) static HashstarvationNudgeKey(Hash sourceHash, Hash destHash) Which hash a pool-starvation nudge must be addressed to.Methods inherited from class JobImpl
dropped, getContext, getJobId, getMadeReadyOn, getTiming, madeReady, requeue, toStringModifier and TypeMethodDescriptionvoiddropped()Default implementation does nothing.final RouterContextThe router context that this job belongs to.longgetJobId()The unique job ID for this job.longThe timestamp when this job was last made ready for execution.The timing object for this job.voidmadeReady(long now) For JobQueue only, not for external use.protected voidrequeue(long delayMs) Requeue this job to run after the specified delay.toString()
Field Details
_log
Logger for this send attempt._cache
Cache consulted for the target's lease set._overallExpiration
private final long _overallExpirationWhen this send attempt gives up entirely._clientMessage
The message being sent._clientMessageId
Id of the message being sent._clientMessageSize
private final int _clientMessageSizeSize of the message in bytes._from
The sending client's destination._to
The target destination._toString
Base32 prefix of the target, for the logs._leaseSet
The target's lease set, if known._lease
The actual lease the message is being routed through._encryptionKey
Actual target encryption key from the LS being used._start
private final long _startWhen the job started._finished
The result reported when the job finishes._leaseSetLookupBegin
private long _leaseSetLookupBeginWhen the lease set lookup started._outTunnel
The outbound tunnel the message was sent on._inTunnel
The inbound tunnel the reply arrived on._wantACK
private boolean _wantACKWhether the client asked for an ack._hashPair
Key used to cache things with, based on source + dest.OVERALL_TIMEOUT_MS_PARAM
Final timeout (in milliseconds) that the outbound message will fail in. This can be overridden in therouter.configor the client's session config (the client's session config takes precedence).- See Also:
OVERALL_TIMEOUT_MS_DEFAULT
private static final long OVERALL_TIMEOUT_MS_DEFAULTDefault overall send timeout, one minute.- See Also:
OVERALL_TIMEOUT_MS_MIN
private static final long OVERALL_TIMEOUT_MS_MINShortest overall send timeout allowed.- See Also:
OVERALL_TIMEOUT_MS_MAX
private static final long OVERALL_TIMEOUT_MS_MAXLongest overall send timeout allowed.- See Also:
LS_LOOKUP_TIMEOUT
private static final long LS_LOOKUP_TIMEOUTTimeout for the lease set lookup.- See Also:
OVERALL_TIMEOUT_NOLS_MIN
private static final long OVERALL_TIMEOUT_NOLS_MINMinimum overall timeout when a lookup is needed.- See Also:
REPLY_TIMEOUT_MS_MIN
private static final long REPLY_TIMEOUT_MS_MINMinimum time left for the reply after sending.- See Also:
RATCHET_REPLY_TIMEOUT_MS_MIN
private static final long RATCHET_REPLY_TIMEOUT_MS_MINLeast time left for the ratchet reply callback, longer than the send timeout so a success after a failure still counts.- See Also:
BUNDLE_REPLY_LEASESET
NOTE: Changed as of 0.9.2.Defaults to true.
If the client's config specifies shouldBundleReplyInfo=true, messages sent from that client to any peers will periodically include the sending destination's current LeaseSet (allowing the recipient to reply without having to do a full netDb lookup). This should improve performance during the initial negotiations.
For clients that do not publish their LeaseSet, this option must be true for any reply to be possible.
Setting to "false" may save significant outbound bandwidth, especially if the client is configured with a large number of inbound tunnels (Leases). If replies are still required, this may shift the bandwidth burden to the far-end client and the floodfill.
There are several cases where "false" is may be appropriate:
- Unidirectional communication, no reply required
- LeaseSet is published and higher reply latency is acceptable
- LeaseSet is published, client is a "server", all connections are inbound so the connecting far-end destination obviously has the leaseset already. Connections are either short, or it is acceptable for latency on a long-lived connection to temporarily increase while the other end re-fetches the LeaseSet after expiration. HTTP servers may fit these requirements.
- See Also:
REPLY_REQUEST_INTERVAL
private static final int REPLY_REQUEST_INTERVALInterval between cached reply requests.- See Also:
MIN_TUNNEL_USAGE
private static final long MIN_TUNNEL_USAGEMinimum time in ms to keep using the same outbound tunnel for a given source-destination pair, to prevent rapid tunnel switching that causes out-of-order delivery and unnecessary NACKs in the streaming layer.- See Also:
MAX_ROTATION_CANDIDATES
private static final int MAX_ROTATION_CANDIDATESHow many pool picks to sample when gathering rotation candidates for a fresh connection. With the default client pool of two tunnels a single extra pick almost always yields the alternative. A small bound keeps the hot path cheap.- See Also:
RATES
private static final long[] RATESRate stat period for the send statistics.STALL_ROTATION_COOLDOWN_MS
private static final long STALL_ROTATION_COOLDOWN_MSDefault cooldown a stall mark stays in effect: how long the router prefers another outbound tunnel for a destination that stopped delivering. Long enough to ride out a bad path, short enough that a briefly-bad tunnel is not withheld from a small pool for long.- See Also:
Constructor Details
OutboundClientMessageOneShotJob
Send it.- Parameters:
ctx- router context supplying the clock, log, and client netdbcache- outbound cache holding the LeaseSet cooldown and last-valid statemsg- may have a null payload for ratchet-layer acks
Method Details
init
Called once at router startup.- Parameters:
ctx- router context whose stat manager receives the statistics
getName
runJob
public void runJob()Description copied from interface:JobActually perform the task. This call blocks until the Job is complete.getDestNickname
getReplyLeaseSet
Reply lease set, checking the cache first.- Parameters:
force- to force including a reply lease set- Returns:
- lease set or null if we should not send the lease set
getNextLease
private int getNextLease()Choose a lease from his leaseset to send the message to.Side effects: Sets _lease. Sets _wantACK if it's new or changed. Sets _encryptionKey.
Does several checks to see if we can actually send to this leaseset, and returns nonzero failure code if unable to.
- Returns:
- 0 on success, or a MessageStatusMessage failure code
send
private void send()Send the message to the specified tunnel by creating a new garlic message containing the (already created) payload clove as well as a new delivery status message. This garlic message is sent out one of our tunnels, destined for the lease (tunnel+router) specified, and the delivery status message is targeting one of our free inbound tunnels as well. We use a new reply selector to keep an eye out for that delivery status message's token.clearCaches
private void clearCaches()Called on failure to give us a better chance of success next time. Of course this is probably 60s too late. And we could pick the bad ones at random again. Or remove entries that were sent and succeeded after this was sent but before this failed. But it's a start.selectOutboundTunnel
Choose our outbound tunnel to send the message through. Sets _wantACK if it's new or changed.- Parameters:
_to- the destination we are sending to- Returns:
- the tunnel or null on failure
selectOutboundTunnel
Pick an arbitrary outbound tunnel to send the message through, or null if there aren't any aroundRather than pick one at random, pick the "closest" to the lease, to minimize network OBEP - IBGW connections? This would also eliminate a connection when OBEP == IBGW. Anonymity issues?
- Returns:
- an outbound tunnel, or null if none are available
selectFreshConnectionTunnel
Try to pick a different outbound tunnel than the one the previous connection to this destination used. Only called for the first message of a new connection (fresh streaming SYN), so nothing is in flight on that connection yet and the swap is transparent. The chosen tunnel is recorded as the cached tunnel with a fresh start time, so all subsequent messages of the stream stay on it (no per-packet flapping).Called with
_cache.tunnelCachelocked.- Returns:
- the rotated tunnel, or null when there is no cached tunnel or it is no longer valid - the caller then falls through to normal selection logic. When rotation is saturated (the pool offered no tunnel distinguishable from the previously-used one), the cached entry is evicted before returning null, so the fall-through re-selects fresh instead of riding the tunnel that stalled the previous connection.
selectInboundTunnel
Pick an arbitrary inbound tunnel for any deliveryStatusMessage to come back in.- Returns:
- an inbound tunnel, or null if none are available
dieFatal
private void dieFatal(int status) Give up the ghost, this message just aint going through. tell the client. This is safe to call multiple times (only tells the client once). We may still succeed later.- Parameters:
status- the MessageStatusMessage.STATUS_SEND_FAILURE_* code to report to the client
isTunnelRelatedFailure
static boolean isTunnelRelatedFailure(int status) Whether the I2CP failure status indicates the outbound tunnel itself rejected the message during dispatch, as opposed to a timeout, destination problem, or reply-path issue.Hard dispatch failures are reported to the pool at the normal removal bar. Statuses 14 (expired) and 16 (no tunnels) are local queue / pool-empty conditions, not a fault of this specific tunnel.
- Parameters:
status- the I2CP MessageStatusMessage failure code- Returns:
- true only for hard outbound-dispatch failures
isPoolStarvationFailure
static boolean isPoolStarvationFailure(int status) Whether the failure status means the destination's pools were empty or starved when the send needed them: 14 (expired while queued — nothing carried it before the deadline) and 16 (no tunnels available). These do not blame the outbound tunnel (seeisTunnelRelatedFailure(int)). They trigger a pool-level ensure nudge instead (see dieFatal).- Parameters:
status- the I2CP MessageStatusMessage failure code- Returns:
- true for pool-starvation statuses only
isStalledForRotation
private boolean isStalledForRotation()Whether the current (source, destination) pair is inside a stall cooldown, and rotation is enabled.Always a hint. When true the caller prefers another tunnel but must still return one. A destination whose every tunnel is inside a cooldown has to keep using one of them, or the send fails outright and a slow path becomes a dead one.
- Returns:
- true if a different tunnel should be preferred
starvationNudgeKey
Which hash a pool-starvation nudge must be addressed to. The tunnel pool maps are keyed by the local client (source) destination hash —TunnelPoolManagerregisters pools underclient.calculateHash()for local destinations — so the nudge always goes to the source hash. Passing the remote destination (the pre-fix behavior) finds no pool for every non-local target and the starvation signal silently evaporates.- Parameters:
sourceHash- the local sending destination's hash, or null when unknowndestHash- the remote destination's hash (unused — kept so the source-vs-dest choice is pinned by tests rather than implicit)- Returns:
- the pool-map key to nudge, or null when the source is unknown
isSoftSendFailure
static boolean isSoftSendFailure(int status) Whether the status is a best-effort send timeout (3) after a successful dispatch. Reported to the pool at a higher removal bar so a congested destination or slow reply path cannot cascade-kill healthy tunnels, while a tunnel that times out repeatedly still rotates out before peers accumulate dozens of unreported failures.- Parameters:
status- the I2CP MessageStatusMessage failure code- Returns:
- true for soft send timeouts only
shouldBlameOutboundTunnel
static boolean shouldBlameOutboundTunnel(int status, boolean locallyHosted) Whether a failed send should be charged to the outbound tunnel.Pure, so the blame rule is testable without a router.
A destination this router hosts produces its own reply, so a missing one is our own fault — a saturated handler pool, an aborted body, a slow upstream — and says nothing about the tunnel that delivered the request. Charging those failures to the tunnel makes the pool condemn its own healthy egress. The resulting starvation guarantees the next request fails too, so the service can never recover.
- Parameters:
status- the I2CP MessageStatusMessage failure codelocallyHosted- true if this router hosts the destination- Returns:
- true if the outbound tunnel should be reported as failing
isLocallyHostedDestination
Whether this router hosts the given destination.The registry lookup is not pure, so it is kept out of
shouldBlameOutboundTunnel(int, boolean)and pinned by tests at the decision level instead.- Parameters:
to- destination the message was sent to; may be null- Returns:
- true if the destination is a local service
buildClove
Build the payload clove that will be used for all of the messages, placing the clove in the status structure.- Returns:
- null on failure