Network Module
The Network module provides comprehensive control over HTTP/HTTPS traffic, including monitoring requests and responses, intercepting network calls, and capturing response bodies.
Overview
The Network module enables you to:
- Monitor all network requests and responses
- Intercept requests before they're sent
- Modify or block network traffic
- Capture request and response bodies
- Handle authentication challenges
- Track network errors
Accessing the Module
NetworkModule network = driver.Network;
Timeout and Cancellation
All commands in this module accept optional timeoutOverride and CancellationToken parameters. Use timeoutOverride to set a per-command timeout (defaults to BiDiDriver.DefaultCommandTimeout when omitted). Use CancellationToken for cooperative cancellation. See the API Design Guide for details and examples.
Monitoring Network Traffic
Basic Response Monitoring
// Add observer
driver.Network.OnResponseCompleted.AddObserver((ResponseCompletedEventArgs e) =>
{
Console.WriteLine($"URL: {e.Response.Url}");
Console.WriteLine($"Status: {e.Response.Status} {e.Response.StatusText}");
});
// Subscribe to events
SubscribeCommandParameters subscribe =
new SubscribeCommandParameters(driver.Network.OnResponseCompleted.EventName);
await driver.Session.SubscribeAsync(subscribe);
// Navigate - events will fire for all requests
await driver.BrowsingContext.NavigateAsync(
new NavigateCommandParameters(contextId, "https://example.com"));
Monitor Request Details
driver.Network.OnBeforeRequestSent.AddObserver((BeforeRequestSentEventArgs e) =>
{
Console.WriteLine($"Request: {e.Request.Method} {e.Request.Url}");
Console.WriteLine("Headers:");
foreach (ReadOnlyHeader header in e.Request.Headers)
{
Console.WriteLine($" {header.Name}: {header.Value.Value}");
}
});
SubscribeCommandParameters subscribe =
new SubscribeCommandParameters(driver.Network.OnBeforeRequestSent.EventName);
await driver.Session.SubscribeAsync(subscribe);
Filter by Content Type
// Find Content-Type header
driver.Network.OnResponseCompleted.AddObserver((ResponseCompletedEventArgs e) =>
{
ReadOnlyHeader? contentType = e.Response.Headers
.FirstOrDefault(h => h.Name.Equals("content-type", StringComparison.OrdinalIgnoreCase));
if (contentType != null && contentType.Value.Value.Contains("application/json"))
{
Console.WriteLine($"JSON response from: {e.Response.Url}");
}
});
Filter by URL Pattern
driver.Network.OnBeforeRequestSent.AddObserver((BeforeRequestSentEventArgs e) =>
{
if (e.Request.Url.Contains("/api/"))
{
Console.WriteLine($"API call: {e.Request.Url}");
}
});
Network Events
BeforeRequestSent
Fired when a request is about to be sent:
driver.Network.OnBeforeRequestSent.AddObserver((BeforeRequestSentEventArgs e) =>
{
Console.WriteLine($"Method: {e.Request.Method}");
Console.WriteLine($"URL: {e.Request.Url}");
Console.WriteLine($"Request ID: {e.Request.RequestId}");
Console.WriteLine($"Time origin: {e.Request.Timings.TimeOrigin}");
Console.WriteLine($"Is Blocked: {e.IsBlocked}");
});
ResponseStarted
Fired when response headers are received:
driver.Network.OnResponseStarted.AddObserver((ResponseStartedEventArgs e) =>
{
Console.WriteLine($"Status: {e.Response.Status}");
Console.WriteLine($"Headers received for: {e.Response.Url}");
});
ResponseCompleted
Fired when response is fully received:
driver.Network.OnResponseCompleted.AddObserver((ResponseCompletedEventArgs e) =>
{
Console.WriteLine($"Response complete: {e.Response.Url}");
Console.WriteLine($"Bytes received: {e.Response.BytesReceived}");
});
FetchError
Fired when a network error occurs:
driver.Network.OnFetchError.AddObserver((FetchErrorEventArgs e) =>
{
Console.WriteLine($"Network error for: {e.Request.Url}");
Console.WriteLine($"Error: {e.ErrorText}");
});
AuthRequired
Fired when authentication is needed:
driver.Network.OnAuthRequired.AddObserver(async (AuthRequiredEventArgs e) =>
{
// Provide credentials
ContinueWithAuthCommandParameters parameters =
new ContinueWithAuthCommandParameters(e.Request.RequestId)
{
Action = ContinueWithAuthActionType.ProvideCredentials,
Credentials = new AuthCredentials("myuser", "mypassword"),
};
await driver.Network.ContinueWithAuthAsync(parameters);
},
ObservableEventHandlerOptions.RunHandlerAsynchronously);
Intercepting Network Traffic
Network interception allows you to block, modify, or replace network requests.
Add Intercept
// Specify which phase to intercept
AddInterceptCommandParameters parameters =
new AddInterceptCommandParameters(InterceptPhase.BeforeRequestSent);
// Optional: limit to specific contexts
parameters.Contexts.Add(contextId);
// Optional: URL patterns to intercept
parameters.UrlPatterns.AddRange(
[
new UrlPatternPattern { HostName = "example.com" }
]);
AddInterceptCommandResult result = await driver.Network.AddInterceptAsync(parameters);
string interceptId = result.InterceptId;
Intercept Specific URLs
URL patterns are not wildcard or glob expressions. A UrlPatternPattern compares each part it sets (protocol, host name, port, path, query) for equality with the same part of the request URL, and a part it leaves unset matches anything. A UrlPatternString is a complete URL and matches only that URL. In either form the characters (, ), *, { and } are reserved: the remote end rejects a pattern containing one with an invalid argument error unless it is escaped with a backslash (\). To intercept a kind of resource, such as images, intercept without a pattern and check the request's Destination in the handler.
AddInterceptCommandParameters parameters =
new AddInterceptCommandParameters(InterceptPhase.BeforeRequestSent);
// A pattern has no wildcards. Each part it sets is compared for equality with
// the same part of the request URL, and a part it leaves unset matches anything.
parameters.UrlPatterns.AddRange(
[
// Every request to one host, over any protocol, port, path or query
new UrlPatternPattern { HostName = "images.example.com" },
// Every request to one path, on any host
new UrlPatternPattern { PathName = "/api/data" },
// A string pattern is a complete URL; it matches that URL only
new UrlPatternString("https://example.com/app/config.json"),
]);
await driver.Network.AddInterceptAsync(parameters);
Block Requests
// Add intercept
AddInterceptCommandParameters addIntercept =
new AddInterceptCommandParameters(InterceptPhase.BeforeRequestSent);
addIntercept.UrlPatterns.AddRange(
[
new UrlPatternPattern { HostName = "ads.example.com" },
]);
await driver.Network.AddInterceptAsync(addIntercept);
// Handle intercepted requests
driver.Network.OnBeforeRequestSent.AddObserver(async (BeforeRequestSentEventArgs e) =>
{
if (e.IsBlocked)
{
// Fail the request
FailRequestCommandParameters failParams =
new FailRequestCommandParameters(e.Request.RequestId);
await driver.Network.FailRequestAsync(failParams);
}
},
ObservableEventHandlerOptions.RunHandlerAsynchronously);
Continue Requests
driver.Network.OnBeforeRequestSent.AddObserver(async (BeforeRequestSentEventArgs e) =>
{
if (e.IsBlocked)
{
// Optionally modify request
ContinueRequestCommandParameters parameters =
new ContinueRequestCommandParameters(e.Request.RequestId);
// Add custom header
parameters.Headers = new List<Header>();
foreach (ReadOnlyHeader readOnlyHeader in e.Request.Headers)
{
parameters.Headers.Add(new Header(readOnlyHeader.Name, readOnlyHeader.Value.Value));
}
parameters.Headers.Add(new Header("X-Custom-Header", "MyValue"));
await driver.Network.ContinueRequestAsync(parameters);
}
},
ObservableEventHandlerOptions.RunHandlerAsynchronously);
Provide Custom Response
driver.Network.OnBeforeRequestSent.AddObserver(async (BeforeRequestSentEventArgs e) =>
{
if (e.IsBlocked && e.Request.Url.Contains("/api/data"))
{
// Return custom JSON response
string jsonResponse = "{\"message\": \"Mocked response\"}";
ProvideResponseCommandParameters parameters =
new ProvideResponseCommandParameters(e.Request.RequestId)
{
StatusCode = 200,
ReasonPhrase = "OK",
Body = BytesValue.FromString(jsonResponse),
};
parameters.Headers =
[
new Header("Content-Type", "application/json"),
];
await driver.Network.ProvideResponseAsync(parameters);
}
},
ObservableEventHandlerOptions.RunHandlerAsynchronously);
Continue Responses
ContinueResponseAsync continues a response that the browser has intercepted after it was received
from the server but before it is presented to the browser, optionally overriding the status code,
reason phrase, headers, cookies, or authentication credentials first. It complements
ProvideResponseAsync (which supplies a complete response) and ContinueRequestAsync (which continues
a paused request before it is sent).
Remove Intercept
RemoveInterceptCommandParameters parameters =
new RemoveInterceptCommandParameters(interceptId);
await driver.Network.RemoveInterceptAsync(parameters);
Capturing Response Bodies
To capture response bodies, you must set up a data collector.
Create Data Collector
// Allocate memory for data collection (in bytes)
ulong maxSize = Convert.ToUInt64(Math.Pow(2, 24)); // 16 MB
AddDataCollectorCommandParameters parameters =
new AddDataCollectorCommandParameters(maxSize, DataType.Response);
parameters.Contexts.Add(contextId);
AddDataCollectorCommandResult result =
await driver.Network.AddDataCollectorAsync(parameters);
string collectorId = result.CollectorId;
Get Response Body
driver.Network.OnResponseCompleted.AddObserver(async (ResponseCompletedEventArgs e) =>
{
// Only capture specific responses
if (e.Response.Url.EndsWith(".json"))
{
GetDataCommandParameters getDataParams =
new GetDataCommandParameters(e.Request.RequestId, DataType.Response)
{
CollectorId = collectorId,
DisownCollectedData = true, // Free memory after retrieval
};
GetDataCommandResult dataResult =
await driver.Network.GetDataAsync(getDataParams);
string body = dataResult.Bytes.Value;
capturedBodies.Add(body);
}
},
ObservableEventHandlerOptions.RunHandlerAsynchronously);
Release Response Body Data (DisownDataAsync)
Use DisownDataAsync to release response-body data held by a data collector when you no longer need it. This frees memory without retrieving the data first. Construct DisownDataCommandParameters with the collector ID, request ID, and data type:
DisownDataCommandParameters parameters =
new DisownDataCommandParameters(collectorId, requestId, DataType.Response);
await driver.Network.DisownDataAsync(parameters);
Alternatively, you can set DisownCollectedData = true when calling GetDataAsync to release the data immediately after retrieval. Disowning requires CollectorId to name the collector the data is removed from; without it the command is rejected with invalid argument.
Remove Data Collector
RemoveDataCollectorCommandParameters parameters =
new RemoveDataCollectorCommandParameters(collectorId);
await driver.Network.RemoveDataCollectorAsync(parameters);
Request/Response Headers
Reading Headers
driver.Network.OnResponseCompleted.AddObserver((ResponseCompletedEventArgs e) =>
{
foreach (ReadOnlyHeader header in e.Response.Headers)
{
string name = header.Name;
string value = header.Value.Value;
Console.WriteLine($"{name}: {value}");
}
});
Setting Custom Headers
The intercept-based approach adds or modifies headers for individual requests as they are intercepted. Use this when you need per-request control, conditional header injection, or to modify existing headers:
driver.Network.OnBeforeRequestSent.AddObserver(async (BeforeRequestSentEventArgs e) =>
{
if (e.IsBlocked)
{
ContinueRequestCommandParameters parameters =
new ContinueRequestCommandParameters(e.Request.RequestId);
// Copy existing headers
parameters.Headers = new List<Header>();
foreach (ReadOnlyHeader readOnlyHeader in e.Request.Headers)
{
parameters.Headers.Add(new Header(readOnlyHeader.Name, readOnlyHeader.Value.Value));
}
// Add authorization header
parameters.Headers.Add(new Header("Authorization", "Bearer mytoken"));
await driver.Network.ContinueRequestAsync(parameters);
}
},
ObservableEventHandlerOptions.RunHandlerAsynchronously);
Setting Global Extra Headers
Use SetExtraHeadersAsync to add headers to every request without intercepting traffic. This is simpler and more efficient when you need the same headers (e.g., Authorization, X-API-Key) on all requests. No intercept setup is required.
SetExtraHeadersCommandParameters parameters = new SetExtraHeadersCommandParameters
{
Headers =
{
new Header("Authorization", "Bearer mytoken"),
new Header("X-API-Key", "my-api-key"),
},
};
await driver.Network.SetExtraHeadersAsync(parameters);
Clear Extra Headers
To remove the session-wide (unscoped) extra headers, use SetExtraHeadersCommandParameters.ResetExtraHeaders.
Headers set for specific browsing contexts or user contexts are stored separately and are not affected;
clear them by sending an empty Headers list with the same Contexts or UserContexts populated:
SetExtraHeadersCommandParameters parameters =
SetExtraHeadersCommandParameters.ResetExtraHeaders;
await driver.Network.SetExtraHeadersAsync(parameters);
Cache Behavior
Use SetCacheBehaviorAsync to control whether the browser uses its cache for network requests. This is useful when testing to ensure fresh data (bypass cache) or to restore normal caching behavior.
Bypass Cache
SetCacheBehaviorCommandParameters parameters =
new SetCacheBehaviorCommandParameters(CacheBehavior.Bypass)
{
Contexts = { contextId },
};
await driver.Network.SetCacheBehaviorAsync(parameters);
Restore Default Cache Behavior
SetCacheBehaviorCommandParameters parameters =
new SetCacheBehaviorCommandParameters(CacheBehavior.Default)
{
Contexts = { contextId },
};
await driver.Network.SetCacheBehaviorAsync(parameters);
Cookies (Storage Module)
Cookie commands belong to the Storage module (driver.Storage), not the Network module. They are shown here because network testing frequently involves setting up or inspecting cookies; see the Storage Module guide for full coverage.
Add Cookie
// Use Storage module for cookies
SetCookieCommandParameters parameters = new SetCookieCommandParameters(
new PartialCookie("sessionId", BytesValue.FromString("abc123"), "example.com")
{
Path = "/",
Secure = true,
HttpOnly = true,
SameSite = CookieSameSiteValue.Strict,
});
await driver.Storage.SetCookieAsync(parameters);
Get Cookies
GetCookiesCommandParameters parameters = new GetCookiesCommandParameters();
parameters.Partition = new BrowsingContextPartitionDescriptor(contextId);
GetCookiesCommandResult result = await driver.Storage.GetCookiesAsync(parameters);
foreach (Cookie cookie in result.Cookies)
{
Console.WriteLine($"{cookie.Name}: {cookie.Value.Value}");
Console.WriteLine($" Domain: {cookie.Domain}");
Console.WriteLine($" Expires: {cookie.Expires}");
}
Capturing Traffic
The events, intercepts, and data collectors above are the building blocks for recording traffic. Dramaturge puts them together in NetworkTrafficMonitor, which records each request with its response and bodies and writes the result as a HAR file.
Timing Information
driver.Network.OnResponseCompleted.AddObserver((ResponseCompletedEventArgs e) =>
{
FetchTimingInfo timings = e.Request.Timings;
Console.WriteLine($"Time origin: {timings.TimeOrigin}");
Console.WriteLine($"Request time: {timings.RequestTime}");
Console.WriteLine($"Response start: {timings.ResponseStart}");
Console.WriteLine($"Response end: {timings.ResponseEnd}");
});
Common Patterns
Pattern 1: Collect All Requests
List<RequestData> allRequests = new List<RequestData>();
driver.Network.OnBeforeRequestSent.AddObserver((BeforeRequestSentEventArgs e) =>
{
allRequests.Add(e.Request);
});
SubscribeCommandParameters subscribe =
new SubscribeCommandParameters(driver.Network.OnBeforeRequestSent.EventName);
await driver.Session.SubscribeAsync(subscribe);
await driver.BrowsingContext.NavigateAsync(navParams);
// Wait for requests to complete
await Task.Delay(2000);
Console.WriteLine($"Total requests: {allRequests.Count}");
foreach (var request in allRequests)
{
Console.WriteLine($" {request.Method} {request.Url}");
}
Pattern 2: Block Ad Domains
List<string> adDomains = new List<string>
{
"ads.example.com",
"tracker.example.com"
};
AddInterceptCommandParameters addIntercept =
new AddInterceptCommandParameters(InterceptPhase.BeforeRequestSent);
foreach (string domain in adDomains)
{
addIntercept.UrlPatterns.Add(
new UrlPatternPattern { HostName = domain });
}
await driver.Network.AddInterceptAsync(addIntercept);
driver.Network.OnBeforeRequestSent.AddObserver(async (BeforeRequestSentEventArgs e) =>
{
if (e.IsBlocked)
{
Console.WriteLine($"Blocking: {e.Request.Url}");
await driver.Network.FailRequestAsync(
new FailRequestCommandParameters(e.Request.RequestId));
}
},
ObservableEventHandlerOptions.RunHandlerAsynchronously);
Pattern 3: Mock API Responses
Dictionary<string, string> mockResponses = new Dictionary<string, string>
{
{ "/api/user", "{\"name\": \"Test User\", \"id\": 123}" },
{ "/api/settings", "{\"theme\": \"dark\", \"lang\": \"en\"}" },
};
AddInterceptCommandParameters addIntercept =
new AddInterceptCommandParameters(InterceptPhase.BeforeRequestSent);
await driver.Network.AddInterceptAsync(addIntercept);
driver.Network.OnBeforeRequestSent.AddObserver(async (BeforeRequestSentEventArgs e) =>
{
if (e.IsBlocked)
{
string path = new Uri(e.Request.Url).AbsolutePath;
if (mockResponses.TryGetValue(path, out string? mockData))
{
ProvideResponseCommandParameters parameters =
new ProvideResponseCommandParameters(e.Request.RequestId)
{
StatusCode = 200,
Body = BytesValue.FromString(mockData),
};
parameters.Headers =
[
new Header("Content-Type", "application/json"),
];
await driver.Network.ProvideResponseAsync(parameters);
}
else
{
await driver.Network.ContinueRequestAsync(
new ContinueRequestCommandParameters(e.Request.RequestId));
}
}
},
ObservableEventHandlerOptions.RunHandlerAsynchronously);
Pattern 4: Capture Complete HTTP Transaction
Dictionary<string, HttpTransaction> transactions =
new Dictionary<string, HttpTransaction>();
// Set up data collector
AddDataCollectorCommandParameters addParams =
new AddDataCollectorCommandParameters(Convert.ToUInt64(Math.Pow(2, 26)), DataType.Response);
addParams.Contexts.Add(contextId);
AddDataCollectorCommandResult collectorResult =
await driver.Network.AddDataCollectorAsync(addParams);
string collectorId = collectorResult.CollectorId;
// Capture requests
driver.Network.OnBeforeRequestSent.AddObserver((BeforeRequestSentEventArgs e) =>
{
transactions[e.Request.RequestId] = new HttpTransaction
{
Request = e.Request,
};
});
// Capture responses and bodies
driver.Network.OnResponseCompleted.AddObserver(async (ResponseCompletedEventArgs e) =>
{
if (transactions.TryGetValue(e.Request.RequestId, out HttpTransaction? transaction))
{
transaction.Response = e.Response;
// Get response body
GetDataCommandParameters getDataParams =
new GetDataCommandParameters(e.Request.RequestId, DataType.Response)
{
CollectorId = collectorId,
DisownCollectedData = true,
};
GetDataCommandResult dataResult =
await driver.Network.GetDataAsync(getDataParams);
transaction.ResponseBody = dataResult.Bytes.Value;
}
},
ObservableEventHandlerOptions.RunHandlerAsynchronously);
Best Practices
- Use data collectors wisely: They consume memory; remove when done
- Run async handlers: Network event handlers often need to call commands
- Filter events: Don't process every request if you only need specific ones
- Clean up intercepts: Remove intercepts when no longer needed
- Handle errors: Network operations can fail; use try-catch
- Consider timing: Some network events happen very quickly
Troubleshooting
Events Not Firing
- Ensure you've subscribed to events through Session module
- Check that navigation has actually started
- Verify URL patterns in intercepts are correct
Missing Response Bodies
- Data collector must be added before navigation
- Ensure sufficient memory allocated
- Check that
DisownCollectedDatais set appropriately
Intercepts Not Working
- Verify intercept was added before navigation
- Check URL patterns match the requests
- Ensure
IsBlockedis true in event handler
Next Steps
- Examples: Network Interception: Complete examples
- Examples: Network Monitoring: Practical scenarios
- Storage Module: Working with cookies
- API Reference: Complete API documentation
API Reference
See the API documentation for complete details on all classes and methods in the Network module.