• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Intercepting Network Requests Initiated by the Web Component
2
3The [Network Interception APIs(arkweb_scheme_handler.h)](../reference/apis-arkweb/capi-arkweb-scheme-handler-h.md) are supported to intercept requests sent by **Web** components and provide custom response headers and bodies for intercepted requests.
4
5## Setting Network Interceptors for Web Components
6
7Set **ArkWeb_SchemeHandler** for a specified **Web** component or ServiceWorker. When the **Web** kernel sends a scheme request, the callback of **ArkWeb_SchemeHandler** is triggered. You need to set the network interceptor when the **Web** component is initialized.
8
9**ArkWeb_OnRequestStart** is called when the request starts, and **ArkWeb_OnRequestStop** is called when the request ends.
10
11To intercept the first request sent by a **Web** component, you can use the [initializeWebEngine](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#initializewebengine) method to initialize the **Web** component in advance and then set an interceptor.
12
13  ```c++
14    // Create an ArkWeb_SchemeHandler object.
15    ArkWeb_SchemeHandler *schemeHandler;
16    OH_ArkWeb_CreateSchemeHandler(&schemeHandler);
17
18    // Set the ArkWeb_OnRequestStart and ArkWeb_OnRequestStop callbacks for ArkWeb_SchemeHandler.
19    OH_ArkWebSchemeHandler_SetOnRequestStart(schemeHandler, OnURLRequestStart);
20    OH_ArkWebSchemeHandler_SetOnRequestStop(schemeHandler, OnURLRequestStop);
21
22    // Intercept the request whose scheme is https sent by the Web component whose webTag is scheme-handler.
23    OH_ArkWeb_SetSchemeHandler("https", "scheme-handler", schemeHandler);
24    OH_ArkWebServiceWorker_SetSchemeHandler("https", schemeHandler);
25  ```
26
27You can also intercept requests of built-in schemes of non-web components.
28
29  ```c++
30    // Create an ArkWeb_SchemeHandler object.
31    ArkWeb_SchemeHandler *schemeHandler;
32    OH_ArkWeb_CreateSchemeHandler(&schemeHandler);
33
34    // Set the ArkWeb_OnRequestStart and ArkWeb_OnRequestStop callbacks for ArkWeb_SchemeHandler.
35    OH_ArkWebSchemeHandler_SetOnRequestStart(schemeHandler, OnURLRequestStart);
36    OH_ArkWebSchemeHandler_SetOnRequestStop(schemeHandler, OnURLRequestStop);
37
38    // Intercept the request whose scheme is custom sent by the Web component whose webTag is scheme-handler.
39    OH_ArkWeb_SetSchemeHandler("custom", "scheme-handler", schemeHandler);
40    OH_ArkWebServiceWorker_SetSchemeHandler("custom", schemeHandler);
41  ```
42
43## Rules for Setting a Custom Scheme
44
45To intercept the request of a custom scheme, you need to register the custom scheme with the web kernel before the **Web** component is initialized. Otherwise, the registration fails.
46
47  ```c++
48    // Register the custom scheme with the Web component and specify that this scheme should follow the standard scheme rules, allowing cross-origin requests from this scheme.
49    OH_ArkWeb_RegisterCustomSchemes("custom", ARKWEB_SCHEME_OPTION_STANDARD | ARKWEB_SCHEME_OPTION_CORS_ENABLED);
50    // Register the custom-local scheme with the Web component and specify that this scheme should follow the same rules as the file scheme.
51    OH_ArkWeb_RegisterCustomSchemes("custom-local", ARKWEB_SCHEME_OPTION_LOCAL);
52    // Register custom-csp-bypassing with the Web component and specify that this scheme should follow the standard scheme rules, allowing it to bypass CSP checks.
53    OH_ArkWeb_RegisterCustomSchemes("custom-csp-bypassing", ARKWEB_SCHEME_OPTION_CSP_BYPASSING | ARKWEB_SCHEME_OPTION_STANDARD);
54    // Register custom-isolated with the Web component and specify that requests for this scheme must be initiated from web pages loaded with the same scheme.
55    OH_ArkWeb_RegisterCustomSchemes("custom-isolated", ARKWEB_SCHEME_OPTION_DISPLAY_ISOLATED);
56  ```
57
58The scheme needs to be registered before the **Web** component is initialized, and the network interceptor needs to be set after the **Web** component is initialized. Therefore, you are advised to call the C++ API in **onCreate()** of **EntryAbility** to register the scheme.
59After the scheme is registered, initialize the **Web** component using [initializeWebEngine](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#initializewebengine) and then set the network interceptor.
60
61  ```ts
62    export default class EntryAbility extends UIAbility {
63        onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
64            // Register the scheme configuration.
65            testNapi.registerCustomSchemes();
66            // Initialize the Web Engine, which will initialize the Browser process and create a BrowserContext.
67            webview.WebviewController.initializeWebEngine();
68            // Create and set ArkWeb_SchemeHandler.
69            testNapi.setSchemeHandler();
70        }
71        ...
72    };
73  ```
74
75> **NOTE**
76>
77> **registerCustomSchemes** must be registered before the [initializeWebEngine](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#initializewebengine) method is called.
78
79## Obtaining Information of an Intercepted Request
80
81Use **OH_ArkWebResourceRequest_*** to obtain information of intercepted requests. You can obtain information such as url, method, referrer, headers, and resourceType.
82
83  ```c++
84    char* url;
85    OH_ArkWebResourceRequest_GetUrl(resourceRequest_, &url);
86    OH_ArkWeb_ReleaseString(url);
87
88    char* method;
89    OH_ArkWebResourceRequest_GetMethod(resourceRequest_, &method);
90    OH_ArkWeb_ReleaseString(method);
91
92    int32_t resourceType = OH_ArkWebResourceRequest_GetResourceType(resourceRequest_);
93
94    char* frameUrl;
95    OH_ArkWebResourceRequest_GetFrameUrl(resourceRequest_, &frameUrl);
96    OH_ArkWeb_ReleaseString(frameUrl);
97    ...
98  ```
99
100Uploaded data of **PUT** and **POST** requests can be obtained. Data types such as BYTES, FILE, BLOB, and CHUNKED are supported.
101
102  ```c++
103    // Obtain the uploaded data of the intercepted request.
104    OH_ArkWebResourceRequest_GetHttpBodyStream(resourceRequest(), &stream_);
105    // Set the read callback used to read the uploaded data.
106    OH_ArkWebHttpBodyStream_SetReadCallback(stream_, ReadCallback);
107    // Initialize ArkWeb_HttpBodyStream. Other OH_ArkWebHttpBodyStream* functions need to be called during initialization.
108    OH_ArkWebHttpBodyStream_Init(stream_, InitCallback);
109  ```
110
111## Providing Custom Response Bodies for Intercepted Requests
112
113The network interception provides custom response bodies for intercepted requests in stream mode in the worker thread. You can also use a specific [network errorcode](../reference/apis-arkweb/capi-arkweb-net-error-list-h.md) to end the current intercepted request.
114
115  ```c++
116    // Create a response header for the intercepted request.
117    ArkWeb_Response *response;
118    OH_ArkWeb_CreateResponse(&response);
119
120    // Set the HTTP status code to 200.
121    OH_ArkWebResponse_SetStatus(response, 200);
122    // Set the encoding format of the response body.
123    OH_ArkWebResponse_SetCharset(response, "UTF-8");
124    // Set the size of the response body.
125    OH_ArkWebResponse_SetHeaderByName(response, "content-length", "1024", false);
126    // Pass the response header created for the intercepted request to the Web component.
127    OH_ArkWebResourceHandler_DidReceiveResponse(resourceHandler, response);
128
129    // This function can be called for multiple times, and data can be transferred to the Web component in multiple copies.
130    OH_ArkWebResourceHandler_DidReceiveData(resourceHandler, buffer, bufLen);
131
132    // The response body for reading ends. If you want the request to fail, you can also call OH_ArkWebResourceHandler_DidFailWithError(resourceHandler_, errorCode).
133    // Pass an error code to the Web component and end the request.
134    OH_ArkWebResourceHandler_DidFinish(resourceHandler);
135  ```
136
137## Sample Code
138
139In DevEco Studio, create a default **Native C++** project. You need to prepare an MP4 file named **test.mp4** and place the file in **main/resources/rawfile**.
140
141main/ets/pages/index.ets
142```ts
143import testNapi from 'libentry.so';
144import { webview } from '@kit.ArkWeb';
145import { resourceManager } from '@kit.LocalizationKit';
146
147@Entry
148@Component
149struct Index {
150  mycontroller: webview.WebviewController = new webview.WebviewController("scheme-handler");
151
152  build() {
153    Row() {
154      Column() {
155        Button("goback").onClick( event => {
156          this.mycontroller.backward();
157        })
158
159        Web({ src: $rawfile("test.html"), controller: this.mycontroller})
160          .javaScriptAccess(true)
161          .width('100%')
162          .height('100%')
163          .databaseAccess(true)
164          .fileAccess(false)
165          .domStorageAccess(true)
166          .cacheMode(CacheMode.Default)
167          .onPageBegin( event => {
168            testNapi.initResourceManager(this.getUIContext().getHostContext()!.resourceManager);
169          })
170      }
171      .width('100%')
172    }
173    .height('100%')
174  }
175}
176```
177
178main/ets/entryability/EntryAbility.ets
179```ts
180import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
181import { hilog } from '@kit.PerformanceAnalysisKit';
182import { window } from '@kit.ArkUI';
183import testNapi from 'libentry.so';
184import { webview } from '@kit.ArkWeb';
185
186export default class EntryAbility extends UIAbility {
187    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
188        // Register the configuration of the third-party protocol.
189        testNapi.registerCustomSchemes();
190        // Initialize the Web Engine, which will initialize the Browser process and create a BrowserContext.
191        webview.WebviewController.initializeWebEngine();
192        // Set SchemeHandler.
193        testNapi.setSchemeHandler();
194    }
195
196    onDestroy(): void {
197
198    }
199
200    onWindowStageCreate(windowStage: window.WindowStage): void {
201        windowStage.loadContent('pages/Index', (err, data) => {
202            if (err.code) {
203                return;
204            }
205        });
206    }
207
208    onWindowStageDestroy(): void {
209
210    }
211
212    onForeground(): void {
213
214    }
215
216    onBackground(): void {
217
218    }
219};
220```
221
222main/cpp/hello.cpp
223```c++
224#include "hilog/log.h"
225#include "napi/native_api.h"
226#include "rawfile_request.h"
227#include "rawfile/raw_file_manager.h"
228#include "web/arkweb_scheme_handler.h"
229#include "web/arkweb_net_error_list.h"
230
231#undef LOG_TAG
232#define LOG_TAG "ss-handler"
233
234ArkWeb_SchemeHandler *g_schemeHandler;
235ArkWeb_SchemeHandler *g_schemeHandlerForSW;
236NativeResourceManager *g_resourceManager;
237
238// Register the configuration of the third-party protocol. This API must be called before the Web kernel is initialized. Otherwise, the registration fails.
239static napi_value RegisterCustomSchemes(napi_env env, napi_callback_info info)
240{
241    OH_LOG_INFO(LOG_APP, "register custom schemes");
242    OH_ArkWeb_RegisterCustomSchemes("custom", ARKWEB_SCHEME_OPTION_STANDARD | ARKWEB_SCHEME_OPTION_CORS_ENABLED);
243    OH_ArkWeb_RegisterCustomSchemes("custom-local", ARKWEB_SCHEME_OPTION_LOCAL);
244    OH_ArkWeb_RegisterCustomSchemes("custom-csp-bypassing", ARKWEB_SCHEME_OPTION_CSP_BYPASSING | ARKWEB_SCHEME_OPTION_STANDARD);
245    OH_ArkWeb_RegisterCustomSchemes("custom-isolated", ARKWEB_SCHEME_OPTION_DISPLAY_ISOLATED);
246    return nullptr;
247}
248
249// Callback used when a request is started. Create a RawfileRequest in this function to intercept Web kernel requests.
250void OnURLRequestStart(const ArkWeb_SchemeHandler *schemeHandler,
251                       ArkWeb_ResourceRequest *resourceRequest,
252                       const ArkWeb_ResourceHandler *resourceHandler,
253                       bool *intercept)
254{
255    *intercept = true;
256    RawfileRequest* request = new RawfileRequest(resourceRequest, resourceHandler, g_resourceManager);
257    OH_ArkWebResourceRequest_SetUserData(resourceRequest, request);
258    request->Start();
259}
260
261// Callback used when the request is stopped. In this function, mark RawfileRequest as ended, and ResourceHandler should not be used internally.
262void OnURLRequestStop(const ArkWeb_SchemeHandler *schemeHandler,
263                      const ArkWeb_ResourceRequest *request)
264{
265    if (!request) {
266        OH_LOG_ERROR(LOG_APP, "on request stop request is nullptr.");
267        return;
268    }
269
270    RawfileRequest *rawfileRequest = (RawfileRequest *)OH_ArkWebResourceRequest_GetUserData(request);
271    if (rawfileRequest) {
272        rawfileRequest->Stop();
273    }
274}
275
276void OnURLRequestStartForSW(const ArkWeb_SchemeHandler *schemeHandler,
277                            ArkWeb_ResourceRequest *resourceRequest,
278                            const ArkWeb_ResourceHandler *resourceHandler,
279                            bool *intercept)
280{
281    *intercept = true;
282    RawfileRequest* request = new RawfileRequest(resourceRequest, resourceHandler, g_resourceManager);
283    OH_ArkWebResourceRequest_SetUserData(resourceRequest, request);
284    request->Start();
285}
286
287void OnURLRequestStopForSW(const ArkWeb_SchemeHandler *schemeHandler,
288                           const ArkWeb_ResourceRequest *request)
289{
290    if (!request) {
291        OH_LOG_ERROR(LOG_APP, "on request stop request is nullptr.");
292        return;
293    }
294
295    RawfileRequest *rawfileRequest = (RawfileRequest *)OH_ArkWebResourceRequest_GetUserData(request);
296    if (rawfileRequest) {
297        rawfileRequest->Stop();
298    }
299}
300
301// Set SchemeHandler.
302static napi_value SetSchemeHandler(napi_env env, napi_callback_info info)
303{
304    OH_LOG_INFO(LOG_APP, "set scheme handler");
305    OH_ArkWeb_CreateSchemeHandler(&g_schemeHandler);
306    OH_ArkWeb_CreateSchemeHandler(&g_schemeHandlerForSW);
307
308    OH_ArkWebSchemeHandler_SetOnRequestStart(g_schemeHandler, OnURLRequestStart);
309    OH_ArkWebSchemeHandler_SetOnRequestStop(g_schemeHandler, OnURLRequestStop);
310
311    OH_ArkWebSchemeHandler_SetOnRequestStart(g_schemeHandlerForSW, OnURLRequestStart);
312    OH_ArkWebSchemeHandler_SetOnRequestStop(g_schemeHandlerForSW, OnURLRequestStop);
313
314    OH_ArkWeb_SetSchemeHandler("custom", "scheme-handler", g_schemeHandler);
315    OH_ArkWeb_SetSchemeHandler("custom-csp-bypassing", "scheme-handler", g_schemeHandler);
316    OH_ArkWeb_SetSchemeHandler("custom-isolated", "scheme-handler", g_schemeHandler);
317    OH_ArkWeb_SetSchemeHandler("custom-local", "scheme-handler", g_schemeHandler);
318    OH_ArkWeb_SetSchemeHandler("https", "scheme-handler", g_schemeHandler);
319    OH_ArkWeb_SetSchemeHandler("http", "scheme-handler", g_schemeHandler);
320
321    OH_ArkWebServiceWorker_SetSchemeHandler("https", g_schemeHandlerForSW);
322    return nullptr;
323}
324
325static napi_value InitResourceManager(napi_env env, napi_callback_info info)
326{
327    size_t argc = 2;
328    napi_value argv[2] = {nullptr};
329    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
330    g_resourceManager = OH_ResourceManager_InitNativeResourceManager(env, argv[0]);
331    return nullptr;
332}
333
334EXTERN_C_START
335static napi_value Init(napi_env env, napi_value exports)
336{
337    napi_property_descriptor desc[] = {
338        {"setSchemeHandler", nullptr, SetSchemeHandler, nullptr, nullptr, nullptr, napi_default, nullptr},
339        {"initResourceManager", nullptr, InitResourceManager, nullptr, nullptr, nullptr, napi_default, nullptr},
340        {"registerCustomSchemes", nullptr, RegisterCustomSchemes, nullptr, nullptr, nullptr, napi_default, nullptr}
341    };
342    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
343    return exports;
344}
345EXTERN_C_END
346
347static napi_module demoModule = {
348    .nm_version = 1,
349    .nm_flags = 0,
350    .nm_filename = nullptr,
351    .nm_register_func = Init,
352    .nm_modname = "entry",
353    .nm_priv = ((void*)0),
354    .reserved = { 0 },
355};
356
357extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
358{
359    napi_module_register(&demoModule);
360}
361```
362
363
364main/cpp/CMakeLists.txt
365```text
366# the minimum version of CMake.
367cmake_minimum_required(VERSION 3.4.1)
368project(schemehandler)
369
370set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
371
372if(DEFINED PACKAGE_INFO_FILE)
373    include(${PACKAGE_INFO_FILE})
374endif()
375
376include_directories(${NATIVERENDER_ROOT_PATH}
377                    ${NATIVERENDER_ROOT_PATH}/include)
378
379add_library(entry SHARED rawfile_request.cpp hello.cpp)
380target_link_libraries(entry PUBLIC librawfile.z.so libace_napi.z.so libohweb.so libhilog_ndk.z.so)
381```
382
383main/cpp/types/index.d.ts
384```ts
385export const registerCustomSchemes: () => void;
386export const setSchemeHandler: () => void;
387export const initResourceManager: (resmgr: resourceManager.ResourceManager) => void;
388```
389
390main/cpp/rawfile_request.h
391```c++
392#ifndef RAWFILE_REQUEST_H
393#define RAWFILE_REQUEST_H
394
395#include <mutex>
396#include <string>
397
398#include <rawfile/raw_file_manager.h>
399#include "web/arkweb_scheme_handler.h"
400#include "web/arkweb_net_error_list.h"
401
402class RawfileRequest {
403public:
404    RawfileRequest(const ArkWeb_ResourceRequest *resourceRequest,
405                   const ArkWeb_ResourceHandler *resourceHandler,
406                   const NativeResourceManager* resourceManager);
407    ~RawfileRequest();
408
409    void Start();
410    void Stop();
411    void ReadRawfileDataOnWorkerThread();
412
413    const ArkWeb_ResourceHandler *resourceHandler() { return resourceHandler_; }
414    const ArkWeb_ResourceRequest *resourceRequest() { return resourceRequest_; }
415    const NativeResourceManager *resourceManager() { return resourceManager_; }
416    ArkWeb_Response *response() { return response_; }
417    ArkWeb_HttpBodyStream *stream() { return stream_; }
418    const std::string rawfilePath() { return rawfilePath_; }
419
420    void DidReceiveResponse();
421    void DidReceiveData(const uint8_t *buffer, int64_t bufLen);
422    void DidFinish();
423    void DidFailWithError(ArkWeb_NetError errorCode);
424
425private:
426    const ArkWeb_ResourceRequest *resourceRequest_{nullptr};
427    const ArkWeb_ResourceHandler *resourceHandler_{nullptr};
428    const NativeResourceManager *resourceManager_{nullptr};
429    ArkWeb_Response *response_;
430    bool stopped_{false};
431    std::string rawfilePath_;
432    ArkWeb_HttpBodyStream *stream_{nullptr};
433    std::mutex mutex_;
434};
435
436#endif  // RAWFILE_REQUEST_H
437```
438
439main/cpp/rawfile_request.cpp
440```c++
441#include "rawfile_request.h"
442
443#include "threads.h"
444
445#include "hilog/log.h"
446#include "rawfile/raw_file.h"
447#include "rawfile/raw_file_manager.h"
448
449#undef LOG_TAG
450#define LOG_TAG "ss-handler"
451
452namespace {
453
454uint8_t buffer[1024];
455cnd_t http_body_cnd;
456mtx_t http_body_mtx;
457
458// Read callback of HttpBodyStream.
459void ReadCallback(const ArkWeb_HttpBodyStream  *httpBodyStream, uint8_t* buffer, int bytesRead)
460{
461    OH_LOG_INFO(LOG_APP, "read http body back.");
462    bool isEof = OH_ArkWebHttpBodyStream_IsEof(httpBodyStream);
463    if (!isEof && bytesRead != 0) {
464        memset(buffer, 0, 1000);
465        OH_ArkWebHttpBodyStream_Read(httpBodyStream, buffer, 1000);
466    } else {
467        RawfileRequest *rawfileRequest = (RawfileRequest *)OH_ArkWebHttpBodyStream_GetUserData(httpBodyStream);
468        if (rawfileRequest) {
469            rawfileRequest->ReadRawfileDataOnWorkerThread();
470            cnd_signal(&http_body_cnd);
471        }
472    }
473}
474
475int ReadHttpBodyOnWorkerThread(void* userData)
476{
477    memset(buffer, 0, 1000);
478    ArkWeb_HttpBodyStream *httpBodyStream = (ArkWeb_HttpBodyStream *)userData;
479    OH_ArkWebHttpBodyStream_Read(httpBodyStream, buffer, 1000);
480    cnd_init(&http_body_cnd);
481    mtx_init(&http_body_mtx, mtx_plain);
482    cnd_wait(&http_body_cnd, &http_body_mtx);
483    return 0;
484}
485
486int ReadRawfileOnWorkerThread(void* userData)
487{
488    RawfileRequest * rawfileRequest = (RawfileRequest *)userData;
489    if (rawfileRequest) {
490        rawfileRequest->ReadRawfileDataOnWorkerThread();
491    }
492    return 0;
493}
494
495// Callback used when ArkWeb_HttpBodyStream is initialized.
496void InitCallback(const ArkWeb_HttpBodyStream *httpBodyStream, ArkWeb_NetError result)
497{
498    OH_LOG_INFO(LOG_APP, "init http body stream done %{public}d.", result);
499    bool isChunked = OH_ArkWebHttpBodyStream_IsChunked(httpBodyStream);
500    OH_LOG_INFO(LOG_APP, "http body stream is chunked %{public}d.", isChunked);
501    thrd_t th;
502    if (thrd_create(&th, ReadHttpBodyOnWorkerThread, (void *)httpBodyStream) != thrd_success) {
503        OH_LOG_ERROR(LOG_APP, "create thread failed.");
504        return;
505    }
506
507    if (thrd_detach(th) != thrd_success) {
508        OH_LOG_ERROR(LOG_APP, "detach thread failed.");
509    }
510}
511
512const int blockSize = 1024 * 8;
513
514}  // namespace
515
516RawfileRequest::RawfileRequest(const ArkWeb_ResourceRequest *resourceRequest,
517                               const ArkWeb_ResourceHandler *resourceHandler,
518                               const NativeResourceManager* resourceManager)
519        : resourceRequest_(resourceRequest),
520          resourceHandler_(resourceHandler),
521          resourceManager_(resourceManager) {}
522
523RawfileRequest::~RawfileRequest() {}
524
525void RawfileRequest::Start()
526{
527    OH_LOG_INFO(LOG_APP, "start a rawfile request.");
528    char* url;
529    OH_ArkWebResourceRequest_GetUrl(resourceRequest_, &url);
530    std::string urlStr(url);
531    std::size_t position = urlStr.rfind('/');
532    if (position != std::string::npos) {
533        rawfilePath_ = urlStr.substr(position + 1);
534    }
535    OH_ArkWeb_ReleaseString(url);
536
537    OH_ArkWeb_CreateResponse(&response_);
538    OH_ArkWebResourceRequest_GetHttpBodyStream(resourceRequest(), &stream_);
539    if (stream_) {
540        OH_LOG_ERROR(LOG_APP, "have http body stream");
541        OH_ArkWebHttpBodyStream_SetUserData(stream_, this);
542        OH_ArkWebHttpBodyStream_SetReadCallback(stream_, ReadCallback);
543        OH_ArkWebHttpBodyStream_Init(stream_, InitCallback);
544    } else {
545        thrd_t th;
546        if (thrd_create(&th, ReadRawfileOnWorkerThread, (void *)this) != thrd_success) {
547            OH_LOG_ERROR(LOG_APP, "create thread failed.");
548            return;
549        }
550
551        if (thrd_detach(th) != thrd_success) {
552            OH_LOG_ERROR(LOG_APP, "detach thread failed.");
553        }
554    }
555}
556
557// Read rawfile in the worker thread and return it to the Web kernel using ResourceHandler.
558void RawfileRequest::ReadRawfileDataOnWorkerThread()
559{
560    OH_LOG_INFO(LOG_APP, "read rawfile in worker thread.");
561    const struct UrlInfo {
562        std::string resource;
563        std::string mimeType;
564    } urlInfos[] = {
565        {"test.html", "text/html"},
566        {"video.html", "text/html"},
567        {"isolated.html", "text/html"},
568        {"csp_bypassing.html", "text/html"},
569        {"post_data.html", "text/html"},
570        {"chunked_post_stream.html", "text/html"},
571        {"local.html", "text/html"},
572        {"service_worker.html", "text/html"},
573        {"csp_script.js", "text/javascript"},
574        {"sw.js", "text/javascript"},
575        {"isolated_script.js", "text/javascript"},
576        {"local_script.js", "text/javascript"},
577        {"test.mp4", "video/mp4"},
578        {"xhr", "application/json"}
579    };
580
581    if (!resourceManager()) {
582        OH_LOG_ERROR(LOG_APP, "read rawfile error, resource manager is nullptr.");
583        return;
584    }
585
586    RawFile *rawfile = OH_ResourceManager_OpenRawFile(resourceManager(), rawfilePath().c_str());
587    if (!rawfile) {
588        OH_ArkWebResponse_SetStatus(response(), 404);
589    } else {
590        OH_ArkWebResponse_SetStatus(response(), 200);
591    }
592
593    for (auto &urlInfo : urlInfos) {
594        if (urlInfo.resource == rawfilePath()) {
595            OH_ArkWebResponse_SetMimeType(response(), urlInfo.mimeType.c_str());
596            break;
597        }
598    }
599    OH_ArkWebResponse_SetCharset(response(), "UTF-8");
600
601    long len = OH_ResourceManager_GetRawFileSize(rawfile);
602    OH_ArkWebResponse_SetHeaderByName(response(), "content-length", std::to_string(len).c_str(), false);
603    DidReceiveResponse();
604
605    long consumed = 0;
606    uint8_t buffer[blockSize];
607    while (true) {
608        int ret = OH_ResourceManager_ReadRawFile(rawfile, buffer, blockSize);
609        OH_LOG_INFO(LOG_APP, "read rawfile %{public}d bytes.", ret);
610        if (ret == 0) {
611            break;
612        }
613        consumed += ret;
614        OH_ResourceManager_SeekRawFile(rawfile, consumed, 0);
615        DidReceiveData(buffer, ret);
616        memset(buffer, 0, blockSize);
617    }
618
619    OH_ResourceManager_CloseRawFile(rawfile);
620    DidFinish();
621}
622
623void RawfileRequest::Stop()
624{
625    OH_LOG_INFO(LOG_APP, "stop the rawfile request.");
626    std::lock_guard<std::mutex> guard(mutex_);
627    stopped_ = true;
628    if (response_) {
629        OH_ArkWeb_DestroyResponse(response_);
630    }
631    OH_ArkWebResourceRequest_Destroy(resourceRequest_);
632    OH_ArkWebResourceHandler_Destroy(resourceHandler_);
633}
634
635void RawfileRequest::DidReceiveResponse()
636{
637    OH_LOG_INFO(LOG_APP, "did receive response.");
638    std::lock_guard<std::mutex> guard(mutex_);
639    if (!stopped_) {
640        OH_ArkWebResourceHandler_DidReceiveResponse(resourceHandler_, response_);
641    }
642}
643
644void RawfileRequest::DidReceiveData(const uint8_t *buffer, int64_t bufLen)
645{
646    OH_LOG_INFO(LOG_APP, "did receive data.");
647    std::lock_guard<std::mutex> guard(mutex_);
648    if (!stopped_) {
649        OH_ArkWebResourceHandler_DidReceiveData(resourceHandler_, buffer, bufLen);
650    }
651}
652
653void RawfileRequest::DidFinish()
654{
655    OH_LOG_INFO(LOG_APP, "did finish.");
656    std::lock_guard<std::mutex> guard(mutex_);
657    if (!stopped_) {
658        OH_ArkWebResourceHandler_DidFinish(resourceHandler_);
659    }
660}
661
662void RawfileRequest::DidFailWithError(ArkWeb_NetError errorCode)
663{
664    OH_LOG_INFO(LOG_APP, "did finish with error %{public}d.", errorCode);
665    if (!stopped_) {
666        OH_ArkWebResourceHandler_DidFailWithError(resourceHandler_, errorCode);
667    }
668}
669```
670
671main/resources/rawfile/test.html
672```html
673<html>
674<head>
675<meta name="viewport" content="width=device-width,initial-scale=1">
676</head>
677
678<body>
679<h1> Network Interception Test Demo</h1>
680<a href="https://www.example.com/video.html"> Intercept the video resource request and read the local MP4 file.</a><br/>
681<a href="https://www.example.com/csp_bypassing.html">The third-party protocol bypasses the CSP check and this request is successfully intercepted.</a><br/>
682<a href="https://www.example.com/isolated.html">Intercept the third-party protocol with ISOLATED attribute.</a><br/>
683<a href="https://www.example.com/local.html">Intercept the third-party protocol with LOCAL attribute.</a><br/>
684<a href="https://www.example.com/service_worker.html"> Intercept the request triggered by the service worker.</a><br/>
685<a href="https://www.example.com/post_data.html">Read the HTTP body stream of BLOB type.</a><br/>
686<a href="https://www.example.com/chunked_post_stream.html">Read the http body stream of chunked type.</a>
687</body>
688</html>
689```
690
691main/resources/rawfile/cat.svg
692```
693<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13.37 10.79"><path d="M12.8 10.18l-.8-.8c-.98-.8-.86-1.92-.87-2.04-.02-.1-.02-.58.02-.74.04-.15 0-.32 0-.32.28-1.18 1.2-.85 1.2-.85.38.04.4-.33.4-.33.25-.13.2-.4.2-.4l-.47-.48c-.18-.48-.7-.6-.7-.6.08-.48-.17-.78-.17-.78-.03.14-.58.72-.62.73-.63.15-.43.26-.83.55-.4.28-1.26.63-1.64.43-.37-.2-3.5-.5-4.86-.5-.4 0-.7.1-.95.2-.23-.16-.52-.52-.73-1.02-.3-.74-.36-1.48-.12-1.98.13-.27.28-.42.44-.45.23-.05.52.16.6.24.17.14.42.13.56-.03.15-.15.14-.4-.02-.55C3.38.4 2.8-.1 2.14.02c-.42.08-.76.38-1 .9-.34.7-.3 1.66.1 2.6.18.44.47.93.83 1.25-.1.13-.13.23-.13.23-.12.27-.44.9-.33 1.45.13.56-.22.82-.3.88-.05.07-.73.47-.73.47L0 9.78c-.08.38.43.6.43.6.18-.03.2-.63.2-.63l.44-1.04 1.66-.6s0 .7-.02.83-.1.35-.1.35c.08.46 1.2 1.5 1.2 1.5h.85v-.26c-.07-.3-.5-.16-.5-.16l-.62-.95c.66-.5.93-1.38.93-1.38.3.26 1.8-.22 1.8-.22l.9.1-.25 2.1c-.07.5.05.68.05.68h.4c.3 0 .48.03.48-.27 0-.28-.4-.23-.4-.23l1-1.95c.93-.58 1.53.26 1.53.26l.05.3c.37.53 2.38 1.9 2.38 1.9h1v-.3c-.18-.32-.6-.2-.6-.2z"/></svg>
694```
695
696main/resources/rawfile/csp_bypassing.html
697```html
698<html>
699<head>
700<meta name="viewport" content="width=device-width,initial-scale=1">
701<meta http-equiv="Content-Security-Policy" content="default-src 'self'; media-src 'self'">
702</head>
703<body>
704<p>scheme: custom-csp-bypassing</p>
705<p>options: ARKWEB_SCHEME_OPTION_CSP_BYPASSING | ARKWEB_SCHEME_OPTION_STANDARD</p>
706<script src="custom-csp-bypassing://www.example.com/csp_script.js"></script>
707</body>
708</html>
709```
710
711main/resources/rawfile/csp_script.js
712```js
713const body = document.body;
714const element = document.createElement('div');
715element.textContent = 'csp_script.js bypass the csp rules';
716body.appendChild(element);
717```
718
719main/resources/rawfile/isolated_script.js
720```js
721const element = document.getElementById('isolated_test');
722element.textContent = 'isolated_script.js not blocked';
723```
724
725main/resources/rawfile/isolated.html
726```html
727<html>
728<head>
729<meta name="viewport" content="width=device-width,initial-scale=1">
730</head>
731<body>
732<p>scheme: custom-isolated</p>
733<p>options: ARKWEB_SCHEME_OPTION_DISPLAY_ISOLATED</p>
734<div id="isolated_test">isolated_script.js is intercepted</div>
735<script src="custom-isolated://www.example.com/isolated_script.js"></script>
736</body>
737</html>
738```
739
740main/resources/rawfile/local_script.js
741```js
742const element = document.getElementById('local_test');
743element.textContent = 'local_script.js not blocked.';
744```
745
746main/resources/rawfile/local.html
747```html
748<html>
749<head>
750<meta name="viewport" content="width=device-width,initial-scale=1">
751</head>
752<body>
753<p>scheme: custom-local</p>
754<p>options: ARKWEB_SCHEME_OPTION_LOCAL</p>
755<div id="local_test">local_script.js is intercepted</div>
756<script src="custom-local://www.example.com/local_script.js"></script>
757</body>
758</html>
759```
760
761main/resources/rawfile/post_data.html
762```html
763<html>
764<head>
765<meta name="viewport" content="width=device-width,initial-scale=1">
766<script>
767    function textPostXhr(url) {
768        var formData = new FormData();
769        var myBlob = new Blob(["This is my blob content"], {type : "text/plain"});
770        formData.append("upload", myBlob);
771        var xhr = new XMLHttpRequest();
772        xhr.open('POST', url, true);
773        xhr.send(formData);
774        xhr.onreadystatechange = function (err) {
775            console.log(err.target.status);
776        }
777    }
778    function textPutXhr(url) {
779        var formData = new FormData();
780        var myBlob = new Blob(["This is my blob content"], {type : "text/plain"});
781        formData.append("upload", myBlob);
782        var xhr = new XMLHttpRequest();
783        xhr.open('PUT', url, true);
784        xhr.send(formData);
785        xhr.onreadystatechange = function (err) {
786            console.log(err.target.status);
787        }
788    }
789</script>
790</head>
791<body>
792<div onclick="textPostXhr('https://www.example.com/xhr')">test xhr post</div>
793<div onclick="textPutXhr('https://www.example.com/xhr')">test xhr put</div>
794</body>
795</html>
796```
797
798main/resources/rawfile/service_worker.html
799```html
800<html>
801<head>
802<meta name="viewport" content="width=device-width,initial-scale=1">
803<script>
804function registerSuccess() {
805    const body = document.body;
806    const element = document.createElement('div');
807    element.textContent = 'register sw successful.';
808    body.appendChild(element);
809}
810
811navigator.serviceWorker.register('/sw.js')
812    .then(reg => registerSuccess())
813    .catch(error => console.log('failed!', error))
814</script>
815</head>
816<body>
817</body>
818</html>
819```
820
821main/resources/rawfile/sw.js
822```js
823self.addEventListener('install', event => {
824    console.log('v1 installing');
825    event.waitUntil(
826        caches.open('static-v1').then(cache => cache.add('/cat.svg'))
827    );
828});
829
830self.addEventListener('activate', event => {
831    console.log("v1 now ready to handle fetches.");
832});
833```
834
835main/resources/rawfile/video.html
836```html
837<html>
838<head>
839<meta name="viewport" content="width=device-width,initial-scale=1">
840</head>
841<body>
842<video width="400" height="400" controls>
843    <source src="https://www.example.com/test.mp4" type="video/mp4">
844</video>
845</body>
846</html>
847```
848
849main/resources/rawfile/chunked_post_stream.html
850```html
851<html>
852<head>
853<meta name="viewport" content="width=device-width,initial-scale=1">
854</head>
855<script>
856let uploaded = 0;
857let buf = new Uint8Array(1024 * 50);
858let start = Date.now();
859var rs = new ReadableStream({
860    pull(ctrl) {
861        uploaded += buf.byteLength;
862        crypto.getRandomValues(buf);
863        ctrl.enqueue(buf);
864        if ((start + 1000) < Date.now()) ctrl.close();
865    }
866});
867
868function test() {
869    fetch('https://www.example.com/xhr', {
870        method: 'POST',
871        body: rs,
872        duplex: 'half'
873    }).then(r => r.json()).then(console.log);
874}
875</script>
876<body>
877<div onclick="test()">test post chunked http body.</div>
878</body>
879</html>
880```
881
882main/resources/rawfile/xhr
883```
884{}
885```
886