Customizing HTTP Request Retries

Learn how to take control of HTTP request retries in your application by customizing retry behavior. This guide covers everything from default retry logic to implementing custom strategies, ensuring your requests are handled precisely the way you need.

When working with HTTP requests, there are times when a request might fail due to network issues, server errors, or other unforeseen circumstances.

The retryHttpRequest property gives you control over how these failed requests are handled by allowing you to define custom retry logic.

This guide will walk you through what this property does, why it might be useful for your application, and how to implement it effectively.

What is retryHttpRequest?

The retryHttpRequest property allows you to override the default retry behavior for failed HTTP requests. By providing your own custom handler, you can specify how and when retries should occur, giving you the flexibility to tailor the retry logic to your specific needs.

Why Would You Want to Customize Retry Logic?

There are several reasons why you might want to customize the retry behavior of HTTP requests:

  • Optimizing Retry Attempts: depending on the nature of your application, you may want to retry requests more or fewer times than the default.
  • Custom Delay Logic: you might want to implement a different retry delay strategy, such as an exponential backoff, to better handle network congestion or server load.
  • Handling Specific Errors: certain types of failures might require different retry strategies or even no retries at all.
  • Handling Loss of Connection: you may want to add a custom logic in case the connection is lost.

How Does the Default Retry Logic Work?

If you don’t provide a custom retryHttpRequest handler, the system will use a default retry logic. Here’s how it works:

  • Retry Attempts: the request will be retried up to a maximum of 3 times.
  • Delay Between Retries: the delay between retries is calculated using a custom exponential backoff formula, with a maximum delay of 5 seconds:
    • For each retry attempt, the delay is determined by the formula exp(0.25 * retryCount), which produces a gradually increasing delay.
    • The delay is capped at 5 seconds to ensure retries occur within a reasonable timeframe.
  • Aborting Retries: if the request has already been retried 3 times, no further retries will be made, and the retry process will be aborted.

This default logic is designed to handle common network issues gracefully, but it might not be suitable for all applications.

How to Use retryHttpRequest

Using the retryHttpRequest property involves defining a custom handler that specifies your retry logic. Here’s how you can set it up.

Assign the Handler via a closure

You will assign a closure to the retryHttpRequest property of your network config. The closure will conform to the RetryHttpRequestHandler type and will contain your custom retry logic.

playerConfig.networkConfig.retryHttpRequest = { requestType, retryCount, response, retryHandler, abortHandler in
    // Retry up to 3 times
    guard retryCount <= 3 else {
        // Abort retrying when the maximum retry count is reached
        abortHandler()
        return
    }

    // Calculate a custom delay before retrying (e.g., exponential backoff)
    let retryDelay: TimeInterval = exp(0.25 * Double(retryCount))

    // Retry the original request with the calculated delay
    retryHandler(retryDelay, response.request)
}

Understanding the Process

  • Retry Logic: your custom handler allows you to control how many times a request should be retried and how long to wait between retries.
  • Request Identification: the requestType parameter helps you apply specific retry logic based on the type of request.
  • Handling Failures: if the retry count exceeds your threshold, you can use the abortHandler to stop further retries.

Practical Example

Imagine you have an application where certain critical requests should be retried more aggressively than others. Here’s how you could set up a custom retry handler:

struct ContentView: View {
    private let player: Player

    init() {
        let playerConfig = PlayerConfig()
        playerConfig.networkConfig.retryHttpRequest = { requestType, retryCount, response, retryHandler, abortHandler in
            // Retry up to 5 times for critical requests
            let maxRetries = requestType != .mediaPoster ? 5 : 3
            guard retryCount <= maxRetries else {
                abortHandler()
                return
            }

            // Use a custom delay strategy (linear delay instead of exponential)
            let retryDelay: TimeInterval = 2.0 * Double(retryCount)
            retryHandler(retryDelay, response.request)
        }
        player = PlayerFactory.createPlayer(playerConfig: playerConfig)
    }
    ...
}

With this setup, critical requests are retried up to 5 times with a linear delay, while other requests use the default 3 retries.

Limitations

While the retryHttpRequest property is powerful, it does come with some limitations:

  • Supported Request Types: currently key HLS AES (key/hls/aes) requests are not supported, and subtitle media (media/subtitles) requests are only supported in case the subtitles are side-loaded. Unsupported requests won’t be processed through this handler.
  • Time Constraints: be aware of the AVFoundation limit, which allows approximately 20 seconds for retries. If a request isn’t successfully retried within this timeframe, it will fail.