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