网络

本指南展示了如何使用与网络相关的功能,例如代理、网络事件、身份验证、TLS、客户端证书认证等。

可以通过以下方式获取 Network 实例来访问与网络相关的功能:

Java
Kotlin
var network = profile.network();
val network = profile.network()

Accept-Language 请求头 

Fuzio 允许通过 Accept-Language 方法配置 Network.acceptLanguage(String) HTTP 请求头的值。

例如,值 “zh, en-gb;q=0.8, en;q=0.7” 表示:“我首选中文,但也接受英式英语和其他类型的英语”:

Java
Kotlin
network.acceptLanguage("zh, en-gb;q=0.8, en;q=0.7");
network.acceptLanguage("zh, en-gb;q=0.8, en;q=0.7")

服务器白名单 

HTTP 服务器认证白名单 

你可以配置 HTTP 服务器认证白名单。该白名单是一个字符串,用于指定一组用逗号或分号分隔的 URL 列表。例如:

Java
Kotlin
network.httpAuthPreferences().serverWhitelist("*baidu.com,*example.com,*baz");
network.httpAuthPreferences()
    .serverWhitelist("*baidu.com,*example.com,*baz")

HTTP 网络代理白名单 

要配置 HTTP 网络代理白名单,你可以使用以下描述的方法:

Java
Kotlin
network.httpAuthPreferences().delegateWhitelist("*baidu.com,*example.com,*baz");
network.httpAuthPreferences()
    .delegateWhitelist("*baidu.com,*example.com,*baz")

TLS 

证书验证 

默认情况下,Chromium 会验证网页加载期间从网络服务器获取的所有 SSL 证书。Fuzio 允许修改此默认行为并控制验证过程。

要处理证书验证,请使用 VerifyCertificateCallback 回调。在调用此回调之前,Chromium 会验证 SSL 证书并将验证结果提供给回调。根据验证结果,回调会接收到 SSL 证书本身。你可以验证给定的 SSL 证书并通知引擎验证结果。

例如:

Java
Kotlin
network.set(VerifyCertificateCallback.class, (params) -> {
    // 待验证的 SSL 证书。
    var certificate = params.certificate();
    // 默认验证器执行的验证结果。
    var verificationErrors = params.verificationErrors();
    // 应使用默认验证的结果。
    return VerifyCertificateCallback.Response.defaultAction();
});
network.register(VerifyCertificateCallback { params ->
    // 待验证的 SSL 证书。
    val certificate = params.certificate()
    // 默认验证器执行的验证结果。
    val verificationErrors = params.verificationErrors()
    // 应使用默认验证的结果。
    VerifyCertificateCallback.Response.defaultAction()
})

处理无效证书 

默认情况下,Chromium 在加载带有无效 TLS 证书的页面时会显示错误页面。在 Fuzio 中,你可以在 Browser 级别的 CertificateErrorCallback中自定义此行为。当证书确实无效,或者在 VerifyCertificateCallback 中被标记为无效时,都会调用此回调。

你可以选择以下方式之一来处理无效证书:

  • 显示错误页面。
  • 取消导航。
  • 继续导航。

例如:

Java
Kotlin
browser.set(CertificateErrorCallback.class, (params, tell) -> {
    var url = params.url();
    var error = params.error();
    var mainFrame = params.isMainFrame();
    var certificate = params.certificate();

    if (error == CERT_DATE_INVALID) {
        // 允许导航。
        tell.allow();
    } else if (error == CERT_AUTHORITY_INVALID) {
        // 显示错误页面。
        tell.deny();
    } else {
        // 取消导航。
        tell.cancel();
    }
});
browser.register(CertificateErrorCallback { params, tell ->
    val url = params.url()
    val error = params.error()
    val certificate = params.certificate()
    val mainFrame = params.isMainFrame
    when (error) {
        NetError.CERT_DATE_INVALID -> {
            // 允许导航。
            tell.allow()
        }
        NetError.CERT_AUTHORITY_INVALID -> {
            // 显示错误页面。
            tell.deny()
        }
        else -> {
            // 取消导航。
            tell.cancel()
        }
    }
})

你可以用自己的页面替换默认的 Chromium 错误页面。详情请参阅错误页面指南

客户端证书认证 

Fuzio 支持通过 HTTPS 客户端证书进行身份验证。详情请参阅认证指南

协议 

该库提供了一个 API,允许注册自定义协议并拦截带有标准协议(如 HTTP 或 HTTPS)的 URL 请求。

两种情况下均会拦截对应协议的 URL 请求,并将响应数据模拟为来自 Web 服务器的数据。通过此功能可模拟远程服务器响应,实现类似本地 Web 服务器的功能。

注册自定义协议 

要注册自定义协议,需要在配置 Engine 时通过 EngineOptions.Builder.addScheme() 方法完成。之所以必须在此阶段完成,是因为 Chromium 会在启动期间注册所有协议,并且不允许在初始化后再修改协议。

下面的代码演示了如何注册自定义协议,并关联 InterceptUrlRequestCallback 回调函数,该回调将在浏览器通过指定协议加载资源时被调用:

Java
Kotlin
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("fuz"), 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("fuz"), interceptor)
}

现在,如果你加载 fuz://anyhost/anypage.html,URL 请求将被拦截,并会调用关联的回调。你将看到如下输出:

Custom Scheme

拦截 HTTP/HTTPS 请求 

你可以使用相同的注册自定义协议的方法来拦截标准协议,如 HTTP 或 HTTPS。在这种情况下,你需要如下所示添加协议:

Java
Kotlin
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 API 定义了一组遵循 Web 请求生命周期的网络事件和回调。你可以使用这些事件来观察和分析网络流量。这些回调将允许你拦截、阻止或修改请求。

成功请求的事件生命周期如下所示: Network Events Flow

URL 请求之前 

当一个 HTTP 请求即将发生时,会调用 BeforeUrlRequestCallback 回调。你可以用该回调将请求重定向到另一个地址。例如:

Java
Kotlin
network.set(BeforeUrlRequestCallback.class, (params) ->
        BeforeUrlRequestCallback.Response.redirect("<new-url>"));
network.register(BeforeUrlRequestCallback {
    BeforeUrlRequestCallback.Response.redirect("<new-url>")
})

发送上传数据之前 

在向 Web 服务器发送上传数据之前,会调用 BeforeSendUploadDataCallback 回调。在这里你可以覆盖上传数据。例如:

Java
Kotlin
network.set(BeforeSendUploadDataCallback.class, (params) ->
        BeforeSendUploadDataCallback.Response.override(TextData.of("<text-data>")));
network.register(BeforeSendUploadDataCallback {
    val newData = TextData("<text-data>")
    BeforeSendUploadDataCallback.Response.override(newData)
})

如果请求没有上传数据,则不会调用此回调。

支持以下 UploadData 类型:

  • BytesData 表示一段字节序列,content-type 是可配置的。
  • TextDatatext/plain 内容类型的数据。
  • FormDataapplication/x-www-form-urlencoded 内容类型的数据。
  • MultipartFormDatamultipart/form-data 内容类型的数据。

事务开始之前 

在网络事务开始之前,会调用 BeforeStartTransactionCallback 回调。在该回调中,你可以在请求发送前添加或覆盖 HTTP 请求头。例如:

Java
Kotlin
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)
})

该回调不会捕获以下请求头。此列表可能会变化,且不一定完整:

  • Authorization
  • Cache-Control
  • Connection
  • Content-Length
  • Host
  • If-Modified-Since
  • If-None-Match
  • If-Range
  • Partial-Data
  • Pragma
  • Proxy-Authorization
  • Proxy-Connection
  • Transfer-Encoding

接收请求头 

当收到 HTTP 请求的响应头时,会调用 ReceiveHeadersCallback 回调。在这里你可以添加、修改或移除通过网络接收的 HTTP 头。例如:

Java
Kotlin
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)
})

收到重定向响应码 

当请求收到重定向响应码 3xx 时,会触发 RedirectResponseCodeReceived 事件。在该事件中,你可以获取重定向详情,例如新 URL 和响应码。例如:

Java
Kotlin
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()
}

响应开始 

当收到 URL 响应体的第一个字节时,会触发 ResponseStarted 事件。对于 HTTP 请求,这意味着状态行和响应头已可用。在该事件中,你可以访问对应的请求以及响应码。例如:

Java
Kotlin
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()
}

请求完成 

当 URL 请求成功完成或失败时,会触发 RequestCompleted事件。在该事件中,你可以检查请求是否已开始、获取请求状态详情、访问响应码。例如:

Java
Kotlin
network.on(RequestCompleted.class, (event) -> {
    var urlRequest = event.urlRequest();
    // URL 请求状态的详情。
    var urlRequestStatus = event.status();
    // HTTP 响应码。
    var responseCode = event.responseCode();
});
network.subscribe<RequestCompleted> { event ->
    val urlRequest = event.urlRequest()
    // URL 请求状态的详情。
    val urlRequestStatus = event.status()
    // HTTP 响应码。
    val responseCode = event.responseCode()
}

请求销毁 

当请求被销毁且无法再使用时,会触发 RequestDestroyed 事件。要访问被销毁请求的详情,可使用以下代码:

Java
Kotlin
network.on(RequestDestroyed.class, (event) -> {
    var urlRequest = event.urlRequest();
});
network.subscribe<RequestDestroyed> { event ->
    val urlRequest = event.urlRequest()
}

接收响应字节 

当通过网络接收到一部分 HTTP 响应体时,会触发 ResponseBytesReceived 事件。它允许访问 HTTP 响应体的字节数据:

Java
Kotlin
network.on(ResponseBytesReceived.class, event -> {
    var data = event.data();
});
network.subscribe<ResponseBytesReceived> { event ->
    val data = event.data()
}

响应正文的部分内容可能以随机顺序到达。在使用此事件恢复完整响应时请记住这一点。

文件访问控制 

当你的应用程序使用 file:// 协议加载内容时,你可能需要控制它可以访问哪些本地文件。使用 CanAccessFileCallback 来在引擎每次尝试加载文件时控制访问。默认情况下,如果未设置回调,则授予对所有本地文件的访问权限。

Java
Kotlin
network.set(CanAccessFileCallback.class, params -> {
    if (params.filePath().equals(FILE_PATH)) {
        return Response.can();
    } else {
        return Response.cannot();
    }
});
network.register(CanAccessFileCallback { params ->
    if (params.filePath() == FILE_PATH) {
        Response.can()
    } else {
        Response.cannot()
    }
})

连接状态 

Chromium 会跟踪互联网连接状态。当连接中断后又恢复时,Chromium 会检测到变化并重新加载当前页面。你可以通过以下 API 获取相应通知:

Java
Kotlin
network.on(NetworkChanged.class, e -> {
    // 如果连接类型为 TYPE_NONE,则表示没有连接。
    if (e.connectionType() == ConnectionType.TYPE_NONE) {
        // 网络连接已中断,我们处于离线状态。
    } else {
        // 网络连接已恢复。
    }
});
network.subscribe<NetworkChanged> { event ->
    // 如果连接类型为 TYPE_NONE,则表示没有连接。
    if (event.connectionType() == ConnectionType.TYPE_NONE) {
        // 网络连接已中断,我们处于离线状态。
    } else {
        // 网络连接已恢复。
    }
}
微信咨询

即库客服

微信公众号二维码

技术客服

微信公众号二维码