1# 拦截Web组件发起的网络请求
2
3通过[网络拦截接口(arkweb_scheme_handler.h)](../reference/apis-arkweb/arkweb__scheme__handler_8h.md)对Web组件发出的请求进行拦截,并可以为被拦截的请求提供自定义的响应头以及响应体。
4
5## 为Web组件设置网络拦截器
6
7为指定的Web组件或者ServiceWorker设置ArkWeb_SchemeHandler,当Web内核发出相应scheme请求的时候,会触发ArkWeb_SchemeHandler的回调。需要在Web组件初始化之后设置网络拦截器。
8
9当请求开始的时候会回调ArkWeb_OnRequestStart,请求结束的时候会回调ArkWeb_OnRequestStop。
10
11如果想要拦截Web组件发出的第一个请求,可以通过[initializeWebEngine](../reference/apis-arkweb/js-apis-webview.md#initializewebengine)对Web组件提前进行初始化,然后设置拦截器进行拦截。
12
13  ```c++
14    // 创建一个ArkWeb_SchemeHandler对象。
15    ArkWeb_SchemeHandler *schemeHandler;
16    OH_ArkWeb_CreateSchemeHandler(&schemeHandler);
17
18    // 为ArkWeb_SchemeHandler设置ArkWeb_OnRequestStart与ArkWeb_OnRequestStop回调。
19    OH_ArkWebSchemeHandler_SetOnRequestStart(schemeHandler, OnURLRequestStart);
20    OH_ArkWebSchemeHandler_SetOnRequestStop(schemeHandler, OnURLRequestStop);
21
22    // 拦截webTag为“scheme-handler”的Web组件发出的scheme为“https”的请求。
23    OH_ArkWeb_SetSchemeHandler("https", "scheme-handler", schemeHandler);
24    OH_ArkWebServiceWorker_SetSchemeHandler("https", schemeHandler);
25  ```
26
27也可以拦截非Web组件内置scheme的请求。
28
29  ```c++
30    // 创建一个ArkWeb_SchemeHandler对象。
31    ArkWeb_SchemeHandler *schemeHandler;
32    OH_ArkWeb_CreateSchemeHandler(&schemeHandler);
33
34    // 为ArkWeb_SchemeHandler设置ArkWeb_OnRequestStart与ArkWeb_OnRequestStop回调。
35    OH_ArkWebSchemeHandler_SetOnRequestStart(schemeHandler, OnURLRequestStart);
36    OH_ArkWebSchemeHandler_SetOnRequestStop(schemeHandler, OnURLRequestStop);
37
38    // 拦截webTag为“scheme-handler”的Web组件发出的scheme为“custom”的请求。
39    OH_ArkWeb_SetSchemeHandler("custom", "scheme-handler", schemeHandler);
40    OH_ArkWebServiceWorker_SetSchemeHandler("custom", schemeHandler);
41  ```
42
43## 设置自定义scheme需要遵循的规则
44
45如果要拦截自定义scheme的请求,需要提前将自定义scheme注册到Web内核。需要在Web组件初始化之前进行注册,Web组件初始化后再注册会失败。
46
47  ```c++
48    // 注册“custom“ scheme到Web组件,并指定该scheme需要遵循标准的scheme规则,允许该scheme发出跨域请求。
49    OH_ArkWeb_RegisterCustomSchemes("custom", ARKWEB_SCHEME_OPTION_STANDARD | ARKWEB_SCHEME_OPTION_CORS_ENABLED);
50    // 注册“custom-local” scheme到Web组件,并指定该scheme需要遵循与“file” scheme一样的规则。
51    OH_ArkWeb_RegisterCustomSchemes("custom-local", ARKWEB_SCHEME_OPTION_LOCAL);
52    // 注册“custom-csp-bypassing”到Web组件,并指定该scheme需要遵循标准的scheme规则,允许忽略CSP检查。
53    OH_ArkWeb_RegisterCustomSchemes("custom-csp-bypassing", ARKWEB_SCHEME_OPTION_CSP_BYPASSING | ARKWEB_SCHEME_OPTION_STANDARD);
54    // 注册“custom-isolated”到Web组件,并指定该scheme的请求必须从相同scheme加载的网页中发起。
55    OH_ArkWeb_RegisterCustomSchemes("custom-isolated", ARKWEB_SCHEME_OPTION_DISPLAY_ISOLATED);
56  ```
57
58由于注册scheme需要在Web组件初始化之前进行注册,而网络拦截器需要在Web组件初始化之后设置,建议在EntryAbility的onCreate中调用c++接口注册scheme。
59scheme注册完毕后,通过[initializeWebEngine](../reference/apis-arkweb/js-apis-webview.md#initializewebengine)对Web组件进行初始化,初始化完成后再设置网络拦截器。
60
61  ```ts
62    export default class EntryAbility extends UIAbility {
63        onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
64            // 注册scheme的配置。
65            testNapi.registerCustomSchemes();
66            // 初始化Web组件内核,该操作会初始化Browser进程以及创建BrowserContext。
67            webview.WebviewController.initializeWebEngine();
68            // 创建并设置ArkWeb_SchemeHandler。
69            testNapi.setSchemeHandler();
70        }
71        ...
72    };
73  ```
74
75## 获取被拦截请求的请求信息
76
77通过OH_ArkWebResourceRequest_*接口获取被拦截请求的信息。可以获取url、method、referrer、headers、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
96支持获取PUT/POST类请求的上传数据。数据类型支持BYTES、FILE、BLOB和CHUNKED。
97
98  ```c++
99    // 获取被拦截请求的上传数据。
100    OH_ArkWebResourceRequest_GetHttpBodyStream(resourceRequest(), &stream_);
101    // 设置读取上传数据的读回调。
102    OH_ArkWebHttpBodyStream_SetReadCallback(stream_, ReadCallback);
103    // 初始化ArkWeb_HttpBodyStream,其它OH_ArkWebHttpBodyStream*函数需要在初始化进行调用。
104    OH_ArkWebHttpBodyStream_Init(stream_, InitCallback);
105  ```
106
107## 为被拦截的请求提供自定义的响应体
108
109Web组件的网络拦截支持在worker线程以流的方式为被拦截的请求提供自定义的响应体。也可以以特定的[网络错误码(arkweb_net_error_list.h)](../reference/apis-arkweb/arkweb__net__error__list_8h.md)结束当前被拦截的请求。
110
111  ```c++
112    // 为被拦截的请求创建一个响应头。
113    ArkWeb_Response *response;
114    OH_ArkWeb_CreateResponse(&response);
115
116    // 设置HTTP状态码为200。
117    OH_ArkWebResponse_SetStatus(response, 200);
118    // 设置响应体的编码格式。
119    OH_ArkWebResponse_SetCharset(response, "UTF-8");
120    // 设置响应体的大小。
121    OH_ArkWebResponse_SetHeaderByName(response, "content-length", "1024", false);
122    // 将为被拦截的请求创建的响应头传递给Web组件。
123    OH_ArkWebResourceHandler_DidReceiveResponse(resourceHandler, response);
124
125    // 该函数可以调用多次,数据可以分多份来传递给Web组件。
126    OH_ArkWebResourceHandler_DidReceiveData(resourceHandler, buffer, bufLen);
127
128    // 读取响应体结束,当然如果希望该请求失败的话也可以通过调用OH_ArkWebResourceHandler_DidFailWithError(resourceHandler_, errorCode);
129    // 传递给Web组件一个错误码并结束该请求。
130    OH_ArkWebResourceHandler_DidFinish(resourceHandler);
131  ```
132
133## 完整示例
134
135使用DevEco Studio创建一个默认的Native C++工程,需要提前准备一个mp4文件,命名为test.mp4,将test.mp4放到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        // 注册三方协议的配置。
185        testNapi.registerCustomSchemes();
186        // 初始化Web组件内核,该操作会初始化Browser进程以及创建BrowserContext。
187        webview.WebviewController.initializeWebEngine();
188        // 设置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// 注册三方协议的配置,需要在Web内核初始化之前调用,否则会注册失败。
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// 请求开始的回调,在该函数中我们创建一个RawfileRequest来实现对Web内核请求的拦截。
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// 请求结束的回调,在该函数中我们需要标记RawfileRequest已经结束了,内部不应该再使用ResourceHandler。
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// 设置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// 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// ArkWeb_HttpBodyStream的初始化回调。
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// 在worker线程中读取rawfile,并通过ResourceHandler返回给Web内核。
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> 网络拦截测试demo</h1>
676<a href="https://www.example.com/video.html">拦截视频资源请求,读取本地mp4文件</a><br/>
677<a href="https://www.example.com/csp_bypassing.html">测试三方协议忽略csp检查,并成功拦截</a><br/>
678<a href="https://www.example.com/isolated.html">测试拦截设置ISOLATED属性的三方协议</a><br/>
679<a href="https://www.example.com/local.html">测试拦截设置LOCAL属性的三方协议</a><br/>
680<a href="https://www.example.com/service_worker.html">测试拦截service worker触发的请求</a><br/>
681<a href="https://www.example.com/post_data.html">测试读取blob类型http body stream</a><br/>
682<a href="https://www.example.com/chunked_post_stream.html">测试读取chunked类型http body stream</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 被拦截</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 被拦截</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