Network
This guide shows how to work with the network-related functionality such as proxy, network events, authentication, TLS, client certificate authentication, etc.
The network-related functionality can be accessed through the Network
instance that can be obtained using the following way:
var network = profile.network();
val network = profile.network()
Accept-Language header
JxBrowser allows configuring the Accept-Language
HTTP header value via the Network.acceptLanguage(String)
method.
For example, the “fr, en-gb;q=0.8, en;q=0.7” value would mean: “I prefer French, but will accept British English, and other types of English”:
network.acceptLanguage("fr, en-gb;q=0.8, en;q=0.7");
network.acceptLanguage("fr, en-gb;q=0.8, en;q=0.7")
Server whitelist
HTTP server authorization whitelist
You can configure HTTP server authorization whitelist that represents a string with a list of comma/semicolon separated URLs. For example:
network.httpAuthPreferences().serverWhitelist("*google.com,*example.com,*baz");
network.httpAuthPreferences()
.serverWhitelist("*google.com,*example.com,*baz")
HTTP network delegate whitelist
To configure HTTP network delegate whitelist you can use the approach described below:
network.httpAuthPreferences().delegateWhitelist("*google.com,*example.com,*baz");
network.httpAuthPreferences()
.delegateWhitelist("*google.com,*example.com,*baz")
TLS
Certificate verification
By default, Chromium verifies all SSL certificates obtained from a web server during the web page loading. JxBrowser allows modifying this default behavior and take control over the verification process.
To handle certificate verification use the VerifyCertificateCallback
callback. Before this callback is invoked, Chromium verifies SSL certificate and provides the results of the verification to the callback. With the results of verification, the callback receives the SSL certificate itself. You can verify the given SSL certificate and notify the engine with the verification results.
For example:
network.set(VerifyCertificateCallback.class, (params) -> {
// SSL Certificate to verify.
var certificate = params.certificate();
// The results of the verification performed by default verifier.
var verificationErrors = params.verificationErrors();
// The results of default verification should be used.
return VerifyCertificateCallback.Response.defaultAction();
});
network.register(VerifyCertificateCallback { params ->
// SSL Certificate to verify.
val certificate = params.certificate()
// The results of the verification performed by default verifier.
val verificationErrors = params.verificationErrors()
// The results of default verification should be used.
VerifyCertificateCallback.Response.defaultAction()
})
Client certificate authentication
JxBrowser supports authentication using HTTPS client certificates. Please check the Authentication guide for details.
Schemes
The library provides an API that allows registering the custom schemes and intercept URL requests with the standard schemes such as HTTP or HTTPS.
It both cases the URL requests with the corresponding scheme will be intercepted and response data will be provided as if it was sent from a web server. Using this functionality you can emulate response from a remote server and use a kind of local web server.
Registering custom scheme
To register a custom scheme use the EngineOptions.Builder.addScheme()
method during configuring Engine
. It is required because Chromium registers all schemes during startup and does not allow modifying schemes after it has been initialized.
The following code demonstrates how to register a custom scheme and associate the InterceptUrlRequestCallback
callback that will be invoked every time the browser loads a resource by a URL with the given scheme:
InterceptUrlRequestCallback interceptor = params -> {
var options = UrlRequestJob.Options.newBuilder(HttpStatus.OK)
.addHttpHeader(HttpHeader.of("Content-Type", "text/plain"))
.build();
var job = params.newUrlRequestJob(options);
job.write("Hello!".getBytes());
job.complete();
return InterceptUrlRequestCallback.Response.intercept(job);
};
var options = EngineOptions.newBuilder(renderingMode)
.addScheme(Scheme.of("jxb"), interceptor)
.build();
var engine = Engine.newInstance(options);
val interceptor = InterceptUrlRequestCallback { params ->
val options = UrlRequestJobOptions(
status = HttpStatus.OK,
headers = listOf(HttpHeader("Content-Type", "text/plain"))
)
val job = params.newUrlRequestJob(options).apply {
write("Hello!".toByteArray())
complete()
}
InterceptUrlRequestCallback.Response.intercept(job)
}
val engine = Engine(renderingMode) {
schemes.add(Scheme("jxb"), interceptor)
}
Now, if you load jxb://anyhost/anypage.html
, the URL requests will be intercepted, and the associated callback will be invoked. You will get the following output:
Intercepting HTTP/HTTPS requests
The same approach with registering a custom scheme you can use to intercept the standard schemes such as HTTP or HTTPS. In this case you need to add the corresponding scheme as shown below:
InterceptUrlRequestCallback interceptor = params -> {
var jobOptions = UrlRequestJob.Options.newBuilder(HttpStatus.OK)
.addHttpHeader(HttpHeader.of("Content-Type", "text/plain"))
.build();
var job = params.newUrlRequestJob(jobOptions);
...
return InterceptUrlRequestCallback.Response.intercept(job);
};
var options = EngineOptions.newBuilder(renderingMode)
.addScheme(Scheme.HTTPS, interceptor)
.build();
val interceptor = InterceptUrlRequestCallback { params ->
val options = UrlRequestJobOptions(
status = HttpStatus.OK,
headers = listOf(HttpHeader("Content-Type", "text/plain"))
)
val job = params.newUrlRequestJob(options)
...
InterceptUrlRequestCallback.Response.intercept(job)
}
val engine = Engine(renderingMode) {
schemes.add(Scheme.HTTPS, interceptor)
}
Network events & callbacks
The Network
API defines a set of events and callbacks that follow the life cycle of a web request. You can use these events to observe and analyze traffic. The callbacks will allow you to intercept, block, or modify requests.
The event life cycle of successful requests looks as follows:
Before URL request
The BeforeUrlRequestCallback
callback is invoked when an HTTP request is about to occur. You can use this callback to redirect the request to another location. For example:
network.set(BeforeUrlRequestCallback.class, (params) ->
BeforeUrlRequestCallback.Response.redirect("<new-url>"));
network.register(BeforeUrlRequestCallback {
BeforeUrlRequestCallback.Response.redirect("<new-url>")
})
Before send upload data
The BeforeSendUploadDataCallback
callback is invoked before the upload data is sent to a web server. Here you can override the upload data. For example:
network.set(BeforeSendUploadDataCallback.class, (params) ->
BeforeSendUploadDataCallback.Response.override(TextData.of("<text-data>")));
network.register(BeforeSendUploadDataCallback {
val newData = TextData("<text-data>")
BeforeSendUploadDataCallback.Response.override(newData)
})
This callback will not be called if the request does not have the upload data.
The following UploadData
types are supported:
BytesData
represents a sequence of bytes, thecontent-type
is configurable.TextData
the data of thetext/plain
content type.FormData
the data of theapplication/x-www-form-urlencoded
content type.MultipartFormData
the data of themultipart/form-data
content type.
Before start transaction
The BeforeStartTransactionCallback
callback is invoked before the network transaction starts. In this callback you can
add or override the HTTP headers before they are sent out. For example:
network.set(BeforeStartTransactionCallback.class, (params) -> {
var httpHeaders = new ArrayList<>(params.httpHeaders());
httpHeaders.add(HttpHeader.of("<header-name>", "<header-value>"));
return BeforeStartTransactionCallback.Response.override(httpHeaders);
});
network.register(BeforeStartTransactionCallback { params ->
val customHeader = HttpHeader("<header-name>", "<header-value>")
val httpHeaders = params.httpHeaders() + customHeader
BeforeStartTransactionCallback.Response.override(httpHeaders)
})
This callback doesn’t capture the following headers. This list is a subject to change and may not be complete:
- Authorization
- Cache-Control
- Connection
- Content-Length
- Host
- If-Modified-Since
- If-None-Match
- If-Range
- Partial-Data
- Pragma
- Proxy-Authorization
- Proxy-Connection
- Transfer-Encoding
Receive headers
The ReceiveHeadersCallback
callback is invoked for HTTP requests when the headers have been received. Here you can add, modify, or remove the HTTP headers received over the network. For example:
network.set(ReceiveHeadersCallback.class, (params) -> {
var httpHeaders = new ArrayList<>(params.httpHeaders());
httpHeaders.add(HttpHeader.of("<header-name>", "<header-value>"));
return ReceiveHeadersCallback.Response.override(httpHeaders);
});
network.register(ReceiveHeadersCallback { params ->
val customHeader = HttpHeader("<header-name>", "<header-value>")
val httpHeaders = params.httpHeaders() + customHeader
ReceiveHeadersCallback.Response.override(httpHeaders)
})
Redirect response code received
The RedirectResponseCodeReceived
event is fired when a redirect response code 3xx
has been received for the request. In this event you can get the details about the redirect such as new URL and response code. For example:
network.on(RedirectResponseCodeReceived.class, (event) -> {
var newUrl = event.newUrl();
var responseCode = event.responseCode();
});
network.subscribe<RedirectResponseCodeReceived> { event ->
val newUrl = event.newUrl()
val responseCode = event.responseCode()
}
Response started
The ResponseStarted
event is fired when the first byte of the URL response body has been received. For HTTP requests this means that the status line and the response headers are available. In this event you can access the corresponding request and the response code. For example:
network.on(ResponseStarted.class, (event) -> {
var urlRequest = event.urlRequest();
var responseCode = event.responseCode();
});
network.subscribe<ResponseStarted> { event ->
val urlRequest = event.urlRequest()
val responseCode = event.responseCode()
}
Request completed
The RequestCompleted
event is fired when the URL request has been successfully completed or failed. In this event you can check whether the request has been started at all, get the details of the request status, access the response code. For example:
network.on(RequestCompleted.class, (event) -> {
var urlRequest = event.urlRequest();
// The details of the URL request state.
var urlRequestStatus = event.status();
// The HTTP response code.
var responseCode = event.responseCode();
});
network.subscribe<RequestCompleted> { event ->
val urlRequest = event.urlRequest()
// The details of the URL request state.
val urlRequestStatus = event.status()
// The HTTP response code.
val responseCode = event.responseCode()
}
Request destroyed
The RequestDestroyed
event is fired when the request has been destroyed and it cannot be used anymore. To access the details of the destroyed request use the following code:
network.on(RequestDestroyed.class, (event) -> {
var urlRequest = event.urlRequest();
});
network.subscribe<RequestDestroyed> { event ->
val urlRequest = event.urlRequest()
}
Response bytes received
The ResponseBytesReceived
event is fired when a part of HTTP response body has been received over
the network. It allows accessing the bytes of the HTTP response body:
network.on(ResponseBytesReceived.class, event -> {
var data = event.data();
});
network.subscribe<ResponseBytesReceived> { event ->
val data = event.data()
}
The parts of the response body may come in a random order. Keep this in mind when restoring the full response using this event.
Connection state
Chromium tracks the status of the internet connection. When the connection is dropped and then restored, Chromium detects the change and reloads the currently loaded web page. You can get the corresponding notifications using the following API:
network.on(NetworkChanged.class, e -> {
// If the connection type is TYPE_NONE, there is no connection.
if (e.connectionType() == ConnectionType.TYPE_NONE) {
// The network connection has been dropped. We are offline.
} else {
// The network connection has been restored.
}
});
network.subscribe<NetworkChanged> { event ->
// If the connection type is TYPE_NONE, there is no connection.
if (event.connectionType() == ConnectionType.TYPE_NONE) {
// The network connection has been dropped. We are offline.
} else {
// The network connection has been restored.
}
}