CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-seleniumhq-selenium--selenium-http

HTTP client and server abstractions for Selenium WebDriver communication

Pending
Overview
Eval results
Files

filtering.mddocs/

Filtering System

HTTP request/response filtering system using decorator pattern with built-in filters for user agents, logging, and automatic retries, enabling request/response interception and modification.

Capabilities

Filter Interface

Functional interface for HTTP request/response filtering using decorator pattern, allowing filters to be chained together for complex processing pipelines.

/**
 * Functional interface for HTTP request/response filtering
 * Uses decorator pattern to wrap HttpHandler instances
 * Extends Function<HttpHandler, HttpHandler> for functional composition
 */
@FunctionalInterface
public interface Filter extends Function<HttpHandler, HttpHandler> {
    /**
     * Chains this filter with another filter
     * Creates composite filter where this filter runs first, then next filter
     * @param next Filter to chain after this filter
     * @return Combined filter that applies both filters in sequence
     */
    Filter andThen(Filter next);
    
    /**
     * Terminates filter chain with HttpHandler
     * Creates final HttpHandler with all filters applied
     * @param end Final HttpHandler to handle filtered requests
     * @return HttpHandler with filter chain applied
     */
    HttpHandler andFinally(HttpHandler end);
    
    /**
     * Terminates filter chain with Routable
     * Creates final Routable with all filters applied
     * @param end Final Routable to handle filtered requests
     * @return Routable with filter chain applied
     */
    Routable andFinally(Routable end);
}

Usage Examples:

import org.openqa.selenium.remote.http.*;

// Create individual filters
Filter userAgentFilter = new AddSeleniumUserAgent();
Filter loggingFilter = new DumpHttpExchangeFilter();
Filter retryFilter = new RetryRequest();

// Chain filters together
Filter combinedFilter = userAgentFilter
    .andThen(loggingFilter)
    .andThen(retryFilter);

// Apply filter chain to handler
HttpHandler baseHandler = request -> {
    // Base request handling logic
    return new HttpResponse().setStatus(200);
};

HttpHandler filteredHandler = combinedFilter.andFinally(baseHandler);

// Use filtered handler
HttpRequest request = new HttpRequest(HttpMethod.GET, "/api/test");
HttpResponse response = filteredHandler.execute(request);

// Apply filters to routable
Route route = Route.get("/users/{id}").to(() -> new UserHandler());
Routable filteredRoute = combinedFilter.andFinally(route);

// Custom filter implementation
Filter customFilter = next -> request -> {
    // Pre-processing
    System.out.println("Processing request: " + request.getUri());
    request.addHeader("X-Request-ID", UUID.randomUUID().toString());
    
    // Execute next handler in chain
    HttpResponse response = next.execute(request);
    
    // Post-processing
    response.addHeader("X-Processed-By", "CustomFilter");
    System.out.println("Response status: " + response.getStatus());
    
    return response;
};

// Use custom filter
HttpHandler customHandler = customFilter.andFinally(baseHandler);

AddSeleniumUserAgent Filter

Built-in filter that adds Selenium user agent header to HTTP requests, automatically identifying requests as coming from Selenium WebDriver.

/**
 * Filter that adds Selenium user agent header to requests
 * Automatically applied in default ClientConfig
 */
public class AddSeleniumUserAgent implements Filter {
    /**
     * Static user agent string containing Selenium version and platform info
     * Format: "selenium/{version} ({platform})"
     * Example: "selenium/4.33.0 (java mac)"
     */
    public static final String USER_AGENT;
    
    /**
     * Applies user agent filter to handler
     * Adds User-Agent header if not already present
     * @param next Next handler in filter chain
     * @return HttpHandler that adds user agent header
     */
    public HttpHandler apply(HttpHandler next);
}

Usage Examples:

import org.openqa.selenium.remote.http.*;

// Filter is automatically included in default client config
ClientConfig defaultConfig = ClientConfig.defaultConfig();
// User-Agent header will be automatically added

// Manual usage of the filter
Filter userAgentFilter = new AddSeleniumUserAgent();
HttpHandler handler = userAgentFilter.andFinally(request -> {
    // Check if User-Agent was added
    String userAgent = request.getHeader("User-Agent");
    System.out.println("User-Agent: " + userAgent);
    return new HttpResponse().setStatus(200);
});

// Check the user agent string
System.out.println("Selenium User-Agent: " + AddSeleniumUserAgent.USER_AGENT);

// Combine with other filters
Filter combinedFilter = new AddSeleniumUserAgent()
    .andThen(new DumpHttpExchangeFilter());

// User agent is not added if already present
HttpRequest request = new HttpRequest(HttpMethod.GET, "/api/test");
request.addHeader("User-Agent", "Custom User Agent");

HttpHandler filteredHandler = userAgentFilter.andFinally(req -> {
    // Will still have "Custom User Agent", not overwritten
    return new HttpResponse().setStatus(200);
});

HttpResponse response = filteredHandler.execute(request);

RetryRequest Filter

Filter implementing automatic retry logic for failed requests with exponential backoff strategy for handling transient network failures and server errors.

/**
 * Filter that implements automatic retry logic for failed requests
 * Retries connection failures and server errors (5xx status codes)
 */
public class RetryRequest implements Filter {
    /**
     * Applies retry logic to handler
     * Automatically retries on ConnectionFailedException and 5xx responses
     * Uses exponential backoff strategy with maximum retry attempts
     * @param next Next handler in filter chain
     * @return HttpHandler with retry logic applied
     */
    public HttpHandler apply(HttpHandler next);
}

Usage Examples:

import org.openqa.selenium.remote.http.*;

// Enable retries in client configuration
ClientConfig configWithRetries = ClientConfig.defaultConfig()
    .baseUrl(new URL("https://unreliable-api.example.com"))
    .withRetries();

HttpClient client = HttpClient.Factory.createDefault().createClient(configWithRetries);

// Manual usage of retry filter
Filter retryFilter = new RetryRequest();
HttpHandler unreliableHandler = request -> {
    // Simulate unreliable service
    if (Math.random() < 0.7) {
        throw new ConnectionFailedException("Connection timeout");
    }
    if (Math.random() < 0.5) {
        return new HttpResponse().setStatus(503); // Service unavailable
    }
    return new HttpResponse().setStatus(200);
};

HttpHandler retryHandler = retryFilter.andFinally(unreliableHandler);

// Execute request with retries
HttpRequest request = new HttpRequest(HttpMethod.GET, "/api/data");
try {
    HttpResponse response = retryHandler.execute(request);
    System.out.println("Success after retries: " + response.getStatus());
} catch (ConnectionFailedException e) {
    System.out.println("Failed after all retry attempts");
}

// Combine with other filters
Filter robustFilter = new AddSeleniumUserAgent()
    .andThen(new DumpHttpExchangeFilter())
    .andThen(new RetryRequest());

// Retry behavior:
// - Retries ConnectionFailedException
// - Retries 5xx HTTP status codes
// - Uses exponential backoff
// - Has maximum retry limit
// - Does not retry 4xx client errors

DumpHttpExchangeFilter Filter

Filter for logging HTTP request/response exchanges with configurable log levels for debugging and monitoring HTTP traffic.

/**
 * Filter for logging HTTP request/response exchanges
 * Useful for debugging and monitoring HTTP traffic
 */
public class DumpHttpExchangeFilter implements Filter {
    /**
     * Public logger instance for HTTP exchange logging
     */
    public static final Logger LOG;
    
    /**
     * Creates logging filter with FINER log level
     */
    public DumpHttpExchangeFilter();
    
    /**
     * Creates logging filter with specified log level
     * @param logLevel Logging level for HTTP exchanges
     */
    public DumpHttpExchangeFilter(Level logLevel);
    
    /**
     * Applies logging filter to handler
     * Logs request details before execution and response details after
     * @param next Next handler in filter chain
     * @return HttpHandler with logging applied
     */
    public HttpHandler apply(HttpHandler next);
}

Usage Examples:

import org.openqa.selenium.remote.http.*;
import java.util.logging.Level;
import java.util.logging.Logger;

// Basic logging filter
Filter loggingFilter = new DumpHttpExchangeFilter();

// Logging filter with custom level
Filter infoLoggingFilter = new DumpHttpExchangeFilter(Level.INFO);
Filter warningLoggingFilter = new DumpHttpExchangeFilter(Level.WARNING);

// Configure logger level to see output
Logger httpLogger = DumpHttpExchangeFilter.LOG;
httpLogger.setLevel(Level.FINER);

// Add console handler to see logs
ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(Level.FINER);
httpLogger.addHandler(consoleHandler);

// Apply logging filter
HttpHandler handler = loggingFilter.andFinally(request -> {
    return new HttpResponse()
        .setStatus(200)
        .setContent(Contents.utf8String("Response data"));
});

// Execute request - will log request and response details
HttpRequest request = new HttpRequest(HttpMethod.POST, "/api/users");
request.addHeader("Content-Type", "application/json");
request.setContent(Contents.asJson(Map.of("name", "John Doe")));

HttpResponse response = handler.execute(request);

// Combine with other filters for comprehensive logging
Filter debugFilter = new AddSeleniumUserAgent()
    .andThen(new DumpHttpExchangeFilter(Level.INFO))
    .andThen(new RetryRequest());

// Use in client configuration
ClientConfig debugConfig = ClientConfig.defaultConfig()
    .baseUrl(new URL("https://api.example.com"))
    .withFilter(new DumpHttpExchangeFilter(Level.INFO));

HttpClient debugClient = HttpClient.Factory.createDefault().createClient(debugConfig);

// All requests will be logged with full details:
// - Request method, URI, headers, and content
// - Response status, headers, and content
// - Timing information

Custom Filter Implementation

Creating Custom Filters

import org.openqa.selenium.remote.http.*;
import java.time.Instant;
import java.util.UUID;

// Authentication filter
public class AuthenticationFilter implements Filter {
    private final String authToken;
    
    public AuthenticationFilter(String authToken) {
        this.authToken = authToken;
    }
    
    @Override
    public HttpHandler apply(HttpHandler next) {
        return request -> {
            // Add authentication header
            request.addHeader("Authorization", "Bearer " + authToken);
            return next.execute(request);
        };
    }
}

// Request timing filter
public class TimingFilter implements Filter {
    @Override
    public HttpHandler apply(HttpHandler next) {
        return request -> {
            long startTime = System.currentTimeMillis();
            
            try {
                HttpResponse response = next.execute(request);
                
                long duration = System.currentTimeMillis() - startTime;
                response.setAttribute("request.duration", duration);
                System.out.println("Request to " + request.getUri() + 
                                 " took " + duration + "ms");
                
                return response;
            } catch (Exception e) {
                long duration = System.currentTimeMillis() - startTime;
                System.err.println("Request to " + request.getUri() + 
                                 " failed after " + duration + "ms: " + e.getMessage());
                throw e;
            }
        };
    }
}

// Request ID filter
public class RequestIdFilter implements Filter {
    @Override
    public HttpHandler apply(HttpHandler next) {
        return request -> {
            String requestId = UUID.randomUUID().toString();
            request.addHeader("X-Request-ID", requestId);
            request.setAttribute("request.id", requestId);
            
            HttpResponse response = next.execute(request);
            response.addHeader("X-Request-ID", requestId);
            
            return response;
        };
    }
}

// Rate limiting filter
public class RateLimitFilter implements Filter {
    private final long minIntervalMs;
    private volatile long lastRequestTime = 0;
    
    public RateLimitFilter(long minIntervalMs) {
        this.minIntervalMs = minIntervalMs;
    }
    
    @Override
    public HttpHandler apply(HttpHandler next) {
        return request -> {
            synchronized (this) {
                long now = System.currentTimeMillis();
                long timeSinceLastRequest = now - lastRequestTime;
                
                if (timeSinceLastRequest < minIntervalMs) {
                    long sleepTime = minIntervalMs - timeSinceLastRequest;
                    try {
                        Thread.sleep(sleepTime);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        throw new RuntimeException("Interrupted during rate limiting", e);
                    }
                }
                
                lastRequestTime = System.currentTimeMillis();
            }
            
            return next.execute(request);
        };
    }
}

Using Custom Filters

import org.openqa.selenium.remote.http.*;
import java.net.URL;

// Combine custom filters
Filter authFilter = new AuthenticationFilter("secret-token-123");
Filter timingFilter = new TimingFilter();
Filter rateLimitFilter = new RateLimitFilter(1000); // 1 second between requests
Filter requestIdFilter = new RequestIdFilter();

// Create comprehensive filter chain
Filter comprehensiveFilter = authFilter
    .andThen(requestIdFilter)
    .andThen(timingFilter)
    .andThen(rateLimitFilter)
    .andThen(new DumpHttpExchangeFilter(Level.INFO))
    .andThen(new RetryRequest());

// Use in client configuration
ClientConfig advancedConfig = ClientConfig.defaultConfig()
    .baseUrl(new URL("https://api.example.com"))
    .withFilter(comprehensiveFilter);

HttpClient client = HttpClient.Factory.createDefault().createClient(advancedConfig);

// All requests will have:
// - Authentication header
// - Unique request ID
// - Timing measurement
// - Rate limiting
// - Request/response logging
// - Automatic retries

HttpRequest request = new HttpRequest(HttpMethod.GET, "/users");
HttpResponse response = client.execute(request);

System.out.println("Request ID: " + response.getHeader("X-Request-ID"));
System.out.println("Duration: " + response.getAttribute("request.duration") + "ms");

Filter Order and Composition

The order of filters in the chain matters. Filters are applied in the order they are chained:

// Filter execution order
Filter filterChain = filterA    // Runs first (outermost)
    .andThen(filterB)          // Runs second
    .andThen(filterC);         // Runs third (innermost)

HttpHandler handler = filterChain.andFinally(baseHandler);

/*
 * Request flow:
 * Request -> FilterA -> FilterB -> FilterC -> BaseHandler
 * 
 * Response flow:
 * BaseHandler -> FilterC -> FilterB -> FilterA -> Response
 */

// Example with specific filters
Filter orderedChain = new RequestIdFilter()        // 1. Add request ID
    .andThen(new AuthenticationFilter("token"))    // 2. Add auth header
    .andThen(new TimingFilter())                   // 3. Start timing
    .andThen(new DumpHttpExchangeFilter())         // 4. Log request
    .andThen(new RateLimitFilter(1000))           // 5. Rate limit
    .andThen(new RetryRequest());                 // 6. Handle retries

// This ensures proper layering:
// - Request ID is added before logging
// - Authentication is added before rate limiting
// - Timing includes all processing time
// - Retries happen at the innermost level

Install with Tessl CLI

npx tessl i tessl/maven-org-seleniumhq-selenium--selenium-http

docs

client-config.md

content-handling.md

filtering.md

http-client.md

index.md

request-response.md

routing.md

websocket.md

tile.json