• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 拦截Web组件发起的网络请求
2<!--Kit: ArkWeb-->
3<!--Subsystem: Web-->
4<!--Owner: @aohui-->
5<!--Designer: @yaomingliu-->
6<!--Tester: @ghiker-->
7<!--Adviser: @HelloCrease-->
8
9[网络拦截接口(arkweb_scheme_handler.h)](../reference/apis-arkweb/capi-arkweb-scheme-handler-h.md)可以对Web组件发出的请求进行拦截,并为被拦截的请求提供自定义的响应头以及响应体。
10
11## 为Web组件设置网络拦截器
12
13为指定的Web组件或者ServiceWorker设置ArkWeb_SchemeHandler,当Web内核发出相应scheme请求的时候,会触发ArkWeb_SchemeHandler的回调。需要在Web组件初始化之后设置网络拦截器。
14
15请求开始时回调ArkWeb_OnRequestStart,请求结束时回调ArkWeb_OnRequestStop。
16
17若想要拦截Web组件发出的第一个请求,可以通过[initializeWebEngine](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#initializewebengine)方法提前进行初始化Web组件,再设置拦截器实现拦截。详细代码请参考[完整示例](#完整示例)。
18
19  ```c++
20    // 创建一个ArkWeb_SchemeHandler对象。
21    ArkWeb_SchemeHandler *schemeHandler;
22    OH_ArkWeb_CreateSchemeHandler(&schemeHandler);
23
24    // 为ArkWeb_SchemeHandler设置ArkWeb_OnRequestStart与ArkWeb_OnRequestStop回调。
25    OH_ArkWebSchemeHandler_SetOnRequestStart(schemeHandler, OnURLRequestStart);
26    OH_ArkWebSchemeHandler_SetOnRequestStop(schemeHandler, OnURLRequestStop);
27
28    // 拦截webTag为“scheme-handler”的Web组件发出的scheme为“https”的请求。
29    OH_ArkWeb_SetSchemeHandler("https", "scheme-handler", schemeHandler);
30    OH_ArkWebServiceWorker_SetSchemeHandler("https", schemeHandler);
31  ```
32
33也可以拦截非Web组件内置scheme的请求。
34
35  ```c++
36    // 创建一个ArkWeb_SchemeHandler对象。
37    ArkWeb_SchemeHandler *schemeHandler;
38    OH_ArkWeb_CreateSchemeHandler(&schemeHandler);
39
40    // 为ArkWeb_SchemeHandler设置ArkWeb_OnRequestStart与ArkWeb_OnRequestStop回调。
41    OH_ArkWebSchemeHandler_SetOnRequestStart(schemeHandler, OnURLRequestStart);
42    OH_ArkWebSchemeHandler_SetOnRequestStop(schemeHandler, OnURLRequestStop);
43
44    // 拦截webTag为“scheme-handler”的Web组件发出的scheme为“custom”的请求。
45    OH_ArkWeb_SetSchemeHandler("custom", "scheme-handler", schemeHandler);
46    OH_ArkWebServiceWorker_SetSchemeHandler("custom", schemeHandler);
47  ```
48
49## 设置自定义scheme需要遵循的规则
50
51如果要拦截自定义scheme的请求,需要提前将自定义scheme注册到Web内核。需要在Web组件初始化之前进行注册,初始化后再注册会失败。
52
53  ```c++
54    // 注册“custom“ scheme到Web组件,并指定该scheme需要遵循标准的scheme规则,允许该scheme发出跨域请求。
55    OH_ArkWeb_RegisterCustomSchemes("custom", ARKWEB_SCHEME_OPTION_STANDARD | ARKWEB_SCHEME_OPTION_CORS_ENABLED);
56    // 注册“custom-local” scheme到Web组件,并指定该scheme需要遵循与“file” scheme一样的规则。
57    OH_ArkWeb_RegisterCustomSchemes("custom-local", ARKWEB_SCHEME_OPTION_LOCAL);
58    // 注册“custom-csp-bypassing”到Web组件,并指定该scheme需要遵循标准的scheme规则,允许忽略CSP检查。
59    OH_ArkWeb_RegisterCustomSchemes("custom-csp-bypassing", ARKWEB_SCHEME_OPTION_CSP_BYPASSING | ARKWEB_SCHEME_OPTION_STANDARD);
60    // 注册“custom-isolated”到Web组件,并指定该scheme的请求必须从相同scheme加载的网页中发起。
61    OH_ArkWeb_RegisterCustomSchemes("custom-isolated", ARKWEB_SCHEME_OPTION_DISPLAY_ISOLATED);
62  ```
63
64由于注册scheme需要在Web组件初始化前完成,而网络拦截器需要在Web组件初始化之后设置,建议在EntryAbility的onCreate方法中调用c++接口注册scheme。
65完成scheme注册后,通过[initializeWebEngine](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#initializewebengine)初始化Web组件,然后设置网络拦截器。
66
67  ```ts
68    export default class EntryAbility extends UIAbility {
69        onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
70            // 注册scheme的配置。
71            testNapi.registerCustomSchemes();
72            // 初始化Web组件内核,该操作会初始化Browser进程以及创建BrowserContext。
73            webview.WebviewController.initializeWebEngine();
74            // 创建并设置ArkWeb_SchemeHandler。
75            testNapi.setSchemeHandler();
76        }
77        ...
78    };
79  ```
80
81> **说明:**
82>
83> registerCustomSchemes必须在[initializeWebEngine](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#initializewebengine)方法调用前注册。
84
85## 获取被拦截请求的请求信息
86
87通过OH_ArkWebResourceRequest_*接口获取被拦截请求的信息。可以获取url、method、referrer、headers、resourceType等信息。
88
89  ```c++
90    char* url;
91    OH_ArkWebResourceRequest_GetUrl(resourceRequest_, &url);
92    OH_ArkWeb_ReleaseString(url);
93
94    char* method;
95    OH_ArkWebResourceRequest_GetMethod(resourceRequest_, &method);
96    OH_ArkWeb_ReleaseString(method);
97
98    int32_t resourceType = OH_ArkWebResourceRequest_GetResourceType(resourceRequest_);
99
100    char* frameUrl;
101    OH_ArkWebResourceRequest_GetFrameUrl(resourceRequest_, &frameUrl);
102    OH_ArkWeb_ReleaseString(frameUrl);
103    ...
104  ```
105
106支持获取PUT/POST类请求的上传数据。数据类型支持BYTES、FILE、BLOB和CHUNKED。
107
108  ```c++
109    // 获取被拦截请求的上传数据。
110    OH_ArkWebResourceRequest_GetHttpBodyStream(resourceRequest(), &stream_);
111    // 设置读取上传数据的读回调。
112    OH_ArkWebHttpBodyStream_SetReadCallback(stream_, ReadCallback);
113    // 初始化ArkWeb_HttpBodyStream,其它OH_ArkWebHttpBodyStream*函数需要在初始化进行调用。
114    OH_ArkWebHttpBodyStream_Init(stream_, InitCallback);
115  ```
116
117## 为被拦截的请求提供自定义的响应体
118
119网络拦截支持在worker线程以流方式为被拦截的请求提供自定义的响应体。也可用特定的[网络错误码(arkweb_net_error_list.h)](../reference/apis-arkweb/capi-arkweb-net-error-list-h.md)结束当前被拦截的请求。
120
121  ```c++
122    // 为被拦截的请求创建一个响应头。
123    ArkWeb_Response *response;
124    OH_ArkWeb_CreateResponse(&response);
125
126    // 设置HTTP状态码为200。
127    OH_ArkWebResponse_SetStatus(response, 200);
128    // 设置响应体的编码格式。
129    OH_ArkWebResponse_SetCharset(response, "UTF-8");
130    // 设置响应体的大小。
131    OH_ArkWebResponse_SetHeaderByName(response, "content-length", "1024", false);
132    // 将为被拦截的请求创建的响应头传递给Web组件。
133    OH_ArkWebResourceHandler_DidReceiveResponse(resourceHandler, response);
134
135    // 该函数可以调用多次,数据可以分多份来传递给Web组件。
136    OH_ArkWebResourceHandler_DidReceiveData(resourceHandler, buffer, bufLen);
137
138    // 读取响应体结束,当然如果希望该请求失败的话也可以通过调用OH_ArkWebResourceHandler_DidFailWithError(resourceHandler_, errorCode);
139    // 传递给Web组件一个错误码并结束该请求。
140    OH_ArkWebResourceHandler_DidFinish(resourceHandler);
141  ```
142
143从API version 20开始,如果希望返回一个网络错误码来结束本次网络请求,也可以直接调用OH_ArkWebResourceHandler_DidFailWithErrorV2接口返回一个默认的网络错误码ARKWEB_ERR_CONNECTION_FAILED并结束该网络请求,错误码详情参考[网络错误码(arkweb_net_error_list.h)](../reference/apis-arkweb/capi-arkweb-net-error-list-h.md)。
144  ```c++
145    // 直接返回网络错误码ARKWEB_ERR_CONNECTION_FAILED结束该请求。
146    OH_ArkWebResourceHandler_DidFailWithErrorV2(resourceHandler_, ARKWEB_ERR_FAILED, true);
147  ```
148
149## 完整示例
150
151使用DevEco Studio创建一个默认的Native C++工程,需要提前准备一个mp4文件,命名为test.mp4,并将其放到main/resources/rawfile下。
152
153main/ets/pages/index.ets
154```ts
155import testNapi from 'libentry.so';
156import { webview } from '@kit.ArkWeb';
157import { resourceManager } from '@kit.LocalizationKit';
158
159@Entry
160@Component
161struct Index {
162  mycontroller: webview.WebviewController = new webview.WebviewController("scheme-handler");
163
164  build() {
165    Row() {
166      Column() {
167        Button("goback").onClick( event => {
168          this.mycontroller.backward();
169        })
170
171        Web({ src: $rawfile("test.html"), controller: this.mycontroller})
172          .javaScriptAccess(true)
173          .width('100%')
174          .height('100%')
175          .databaseAccess(true)
176          .fileAccess(false)
177          .domStorageAccess(true)
178          .cacheMode(CacheMode.Default)
179          .onPageBegin( event => {
180            testNapi.initResourceManager(this.getUIContext().getHostContext()!.resourceManager);
181          })
182      }
183      .width('100%')
184    }
185    .height('100%')
186  }
187}
188```
189
190main/ets/entryability/EntryAbility.ets
191```ts
192import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
193import { hilog } from '@kit.PerformanceAnalysisKit';
194import { window } from '@kit.ArkUI';
195import testNapi from 'libentry.so';
196import { webview } from '@kit.ArkWeb';
197
198export default class EntryAbility extends UIAbility {
199    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
200        // 注册三方协议的配置。
201        testNapi.registerCustomSchemes();
202        // 初始化Web组件内核,该操作会初始化Browser进程以及创建BrowserContext。
203        webview.WebviewController.initializeWebEngine();
204        // 设置SchemeHandler。
205        testNapi.setSchemeHandler();
206    }
207
208    onDestroy(): void {
209
210    }
211
212    onWindowStageCreate(windowStage: window.WindowStage): void {
213        windowStage.loadContent('pages/Index', (err, data) => {
214            if (err.code) {
215                return;
216            }
217        });
218    }
219
220    onWindowStageDestroy(): void {
221
222    }
223
224    onForeground(): void {
225
226    }
227
228    onBackground(): void {
229
230    }
231};
232```
233
234main/cpp/hello.cpp
235```c++
236#include "hilog/log.h"
237#include "napi/native_api.h"
238#include "rawfile_request.h"
239#include "rawfile/raw_file_manager.h"
240#include "web/arkweb_scheme_handler.h"
241#include "web/arkweb_net_error_list.h"
242
243#undef LOG_TAG
244#define LOG_TAG "ss-handler"
245
246ArkWeb_SchemeHandler *g_schemeHandler;
247ArkWeb_SchemeHandler *g_schemeHandlerForSW;
248NativeResourceManager *g_resourceManager;
249
250// 注册三方协议的配置,需要在Web内核初始化之前调用,否则会注册失败。
251static napi_value RegisterCustomSchemes(napi_env env, napi_callback_info info)
252{
253    OH_LOG_INFO(LOG_APP, "register custom schemes");
254    OH_ArkWeb_RegisterCustomSchemes("custom", ARKWEB_SCHEME_OPTION_STANDARD | ARKWEB_SCHEME_OPTION_CORS_ENABLED);
255    OH_ArkWeb_RegisterCustomSchemes("custom-local", ARKWEB_SCHEME_OPTION_LOCAL);
256    OH_ArkWeb_RegisterCustomSchemes("custom-csp-bypassing", ARKWEB_SCHEME_OPTION_CSP_BYPASSING | ARKWEB_SCHEME_OPTION_STANDARD);
257    OH_ArkWeb_RegisterCustomSchemes("custom-isolated", ARKWEB_SCHEME_OPTION_DISPLAY_ISOLATED);
258    return nullptr;
259}
260
261// 请求开始的回调,在该函数中我们创建一个RawfileRequest来实现对Web内核请求的拦截。
262void OnURLRequestStart(const ArkWeb_SchemeHandler *schemeHandler,
263                       ArkWeb_ResourceRequest *resourceRequest,
264                       const ArkWeb_ResourceHandler *resourceHandler,
265                       bool *intercept)
266{
267    *intercept = true;
268    RawfileRequest* request = new RawfileRequest(resourceRequest, resourceHandler, g_resourceManager);
269    OH_ArkWebResourceRequest_SetUserData(resourceRequest, request);
270    request->Start();
271}
272
273// 请求结束的回调,在该函数中我们需要标记RawfileRequest已经结束了,内部不应该再使用ResourceHandler。
274void OnURLRequestStop(const ArkWeb_SchemeHandler *schemeHandler,
275                      const ArkWeb_ResourceRequest *request)
276{
277    if (!request) {
278        OH_LOG_ERROR(LOG_APP, "on request stop request is nullptr.");
279        return;
280    }
281
282    RawfileRequest *rawfileRequest = (RawfileRequest *)OH_ArkWebResourceRequest_GetUserData(request);
283    if (rawfileRequest) {
284        rawfileRequest->Stop();
285        delete rawfileRequest;
286    }
287}
288
289void OnURLRequestStartForSW(const ArkWeb_SchemeHandler *schemeHandler,
290                            ArkWeb_ResourceRequest *resourceRequest,
291                            const ArkWeb_ResourceHandler *resourceHandler,
292                            bool *intercept)
293{
294    *intercept = true;
295    RawfileRequest* request = new RawfileRequest(resourceRequest, resourceHandler, g_resourceManager);
296    OH_ArkWebResourceRequest_SetUserData(resourceRequest, request);
297    request->Start();
298}
299
300void OnURLRequestStopForSW(const ArkWeb_SchemeHandler *schemeHandler,
301                           const ArkWeb_ResourceRequest *request)
302{
303    if (!request) {
304        OH_LOG_ERROR(LOG_APP, "on request stop request is nullptr.");
305        return;
306    }
307
308    RawfileRequest *rawfileRequest = (RawfileRequest *)OH_ArkWebResourceRequest_GetUserData(request);
309    if (rawfileRequest) {
310        rawfileRequest->Stop();
311        delete rawfileRequest;
312    }
313}
314
315// 设置SchemeHandler。
316static napi_value SetSchemeHandler(napi_env env, napi_callback_info info)
317{
318    OH_LOG_INFO(LOG_APP, "set scheme handler");
319    OH_ArkWeb_CreateSchemeHandler(&g_schemeHandler);
320    OH_ArkWeb_CreateSchemeHandler(&g_schemeHandlerForSW);
321
322    OH_ArkWebSchemeHandler_SetOnRequestStart(g_schemeHandler, OnURLRequestStart);
323    OH_ArkWebSchemeHandler_SetOnRequestStop(g_schemeHandler, OnURLRequestStop);
324
325    OH_ArkWebSchemeHandler_SetOnRequestStart(g_schemeHandlerForSW, OnURLRequestStart);
326    OH_ArkWebSchemeHandler_SetOnRequestStop(g_schemeHandlerForSW, OnURLRequestStop);
327
328    OH_ArkWeb_SetSchemeHandler("custom", "scheme-handler", g_schemeHandler);
329    OH_ArkWeb_SetSchemeHandler("custom-csp-bypassing", "scheme-handler", g_schemeHandler);
330    OH_ArkWeb_SetSchemeHandler("custom-isolated", "scheme-handler", g_schemeHandler);
331    OH_ArkWeb_SetSchemeHandler("custom-local", "scheme-handler", g_schemeHandler);
332    OH_ArkWeb_SetSchemeHandler("https", "scheme-handler", g_schemeHandler);
333    OH_ArkWeb_SetSchemeHandler("http", "scheme-handler", g_schemeHandler);
334
335    OH_ArkWebServiceWorker_SetSchemeHandler("https", g_schemeHandlerForSW);
336    return nullptr;
337}
338
339static napi_value InitResourceManager(napi_env env, napi_callback_info info)
340{
341    size_t argc = 2;
342    napi_value argv[2] = {nullptr};
343    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
344    g_resourceManager = OH_ResourceManager_InitNativeResourceManager(env, argv[0]);
345    return nullptr;
346}
347
348EXTERN_C_START
349static napi_value Init(napi_env env, napi_value exports)
350{
351    napi_property_descriptor desc[] = {
352        {"setSchemeHandler", nullptr, SetSchemeHandler, nullptr, nullptr, nullptr, napi_default, nullptr},
353        {"initResourceManager", nullptr, InitResourceManager, nullptr, nullptr, nullptr, napi_default, nullptr},
354        {"registerCustomSchemes", nullptr, RegisterCustomSchemes, nullptr, nullptr, nullptr, napi_default, nullptr}
355    };
356    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
357    return exports;
358}
359EXTERN_C_END
360
361static napi_module demoModule = {
362    .nm_version = 1,
363    .nm_flags = 0,
364    .nm_filename = nullptr,
365    .nm_register_func = Init,
366    .nm_modname = "entry",
367    .nm_priv = ((void*)0),
368    .reserved = { 0 },
369};
370
371extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
372{
373    napi_module_register(&demoModule);
374}
375```
376
377
378main/cpp/CMakeLists.txt
379```text
380# the minimum version of CMake.
381cmake_minimum_required(VERSION 3.4.1)
382project(schemehandler)
383
384set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
385
386if(DEFINED PACKAGE_INFO_FILE)
387    include(${PACKAGE_INFO_FILE})
388endif()
389
390include_directories(${NATIVERENDER_ROOT_PATH}
391                    ${NATIVERENDER_ROOT_PATH}/include)
392
393add_library(entry SHARED rawfile_request.cpp hello.cpp)
394target_link_libraries(entry PUBLIC librawfile.z.so libace_napi.z.so libohweb.so libhilog_ndk.z.so)
395```
396
397main/cpp/types/index.d.ts
398```ts
399export const registerCustomSchemes: () => void;
400export const setSchemeHandler: () => void;
401export const initResourceManager: (resmgr: resourceManager.ResourceManager) => void;
402```
403
404main/cpp/rawfile_request.h
405```c++
406#ifndef RAWFILE_REQUEST_H
407#define RAWFILE_REQUEST_H
408
409#include <mutex>
410#include <string>
411
412#include <rawfile/raw_file_manager.h>
413#include "web/arkweb_scheme_handler.h"
414#include "web/arkweb_net_error_list.h"
415
416class RawfileRequest {
417public:
418    RawfileRequest(const ArkWeb_ResourceRequest *resourceRequest,
419                   const ArkWeb_ResourceHandler *resourceHandler,
420                   const NativeResourceManager* resourceManager);
421    ~RawfileRequest();
422
423    void Start();
424    void Stop();
425    void ReadRawfileDataOnWorkerThread();
426
427    const ArkWeb_ResourceHandler *resourceHandler() { return resourceHandler_; }
428    const ArkWeb_ResourceRequest *resourceRequest() { return resourceRequest_; }
429    const NativeResourceManager *resourceManager() { return resourceManager_; }
430    ArkWeb_Response *response() { return response_; }
431    ArkWeb_HttpBodyStream *stream() { return stream_; }
432    const std::string rawfilePath() { return rawfilePath_; }
433
434    void DidReceiveResponse();
435    void DidReceiveData(const uint8_t *buffer, int64_t bufLen);
436    void DidFinish();
437    void DidFailWithError(ArkWeb_NetError errorCode);
438    void DidFailWithErrorV2(ArkWeb_NetError errorCode, bool completeIfNoResponse);
439
440private:
441    const ArkWeb_ResourceRequest *resourceRequest_{nullptr};
442    const ArkWeb_ResourceHandler *resourceHandler_{nullptr};
443    const NativeResourceManager *resourceManager_{nullptr};
444    ArkWeb_Response *response_;
445    bool stopped_{false};
446    std::string rawfilePath_;
447    ArkWeb_HttpBodyStream *stream_{nullptr};
448    std::mutex mutex_;
449};
450
451#endif  // RAWFILE_REQUEST_H
452```
453
454main/cpp/rawfile_request.cpp
455```c++
456#include "rawfile_request.h"
457
458#include "threads.h"
459
460#include "hilog/log.h"
461#include "rawfile/raw_file.h"
462#include "rawfile/raw_file_manager.h"
463
464#undef LOG_TAG
465#define LOG_TAG "ss-handler"
466
467namespace {
468
469uint8_t buffer[1024];
470cnd_t http_body_cnd;
471mtx_t http_body_mtx;
472
473// HttpBodyStream的读回调。
474void ReadCallback(const ArkWeb_HttpBodyStream  *httpBodyStream, uint8_t* buffer, int bytesRead)
475{
476    OH_LOG_INFO(LOG_APP, "read http body back.");
477    bool isEof = OH_ArkWebHttpBodyStream_IsEof(httpBodyStream);
478    if (!isEof && bytesRead != 0) {
479        memset(buffer, 0, 1000);
480        OH_ArkWebHttpBodyStream_Read(httpBodyStream, buffer, 1000);
481    } else {
482        RawfileRequest *rawfileRequest = (RawfileRequest *)OH_ArkWebHttpBodyStream_GetUserData(httpBodyStream);
483        if (rawfileRequest) {
484            rawfileRequest->ReadRawfileDataOnWorkerThread();
485            cnd_signal(&http_body_cnd);
486        }
487    }
488}
489
490int ReadHttpBodyOnWorkerThread(void* userData)
491{
492    memset(buffer, 0, 1000);
493    ArkWeb_HttpBodyStream *httpBodyStream = (ArkWeb_HttpBodyStream *)userData;
494    OH_ArkWebHttpBodyStream_Read(httpBodyStream, buffer, 1000);
495    cnd_init(&http_body_cnd);
496    mtx_init(&http_body_mtx, mtx_plain);
497    cnd_wait(&http_body_cnd, &http_body_mtx);
498    return 0;
499}
500
501int ReadRawfileOnWorkerThread(void* userData)
502{
503    RawfileRequest * rawfileRequest = (RawfileRequest *)userData;
504    if (rawfileRequest) {
505        rawfileRequest->ReadRawfileDataOnWorkerThread();
506    }
507    return 0;
508}
509
510// ArkWeb_HttpBodyStream的初始化回调。
511void InitCallback(const ArkWeb_HttpBodyStream *httpBodyStream, ArkWeb_NetError result)
512{
513    OH_LOG_INFO(LOG_APP, "init http body stream done %{public}d.", result);
514    bool isChunked = OH_ArkWebHttpBodyStream_IsChunked(httpBodyStream);
515    OH_LOG_INFO(LOG_APP, "http body stream is chunked %{public}d.", isChunked);
516    thrd_t th;
517    if (thrd_create(&th, ReadHttpBodyOnWorkerThread, (void *)httpBodyStream) != thrd_success) {
518        OH_LOG_ERROR(LOG_APP, "create thread failed.");
519        return;
520    }
521
522    if (thrd_detach(th) != thrd_success) {
523        OH_LOG_ERROR(LOG_APP, "detach thread failed.");
524    }
525}
526
527const int blockSize = 1024 * 8;
528
529}  // namespace
530
531RawfileRequest::RawfileRequest(const ArkWeb_ResourceRequest *resourceRequest,
532                               const ArkWeb_ResourceHandler *resourceHandler,
533                               const NativeResourceManager* resourceManager)
534        : resourceRequest_(resourceRequest),
535          resourceHandler_(resourceHandler),
536          resourceManager_(resourceManager) {}
537
538RawfileRequest::~RawfileRequest() {
539    if (stream_) {
540        OH_ArkWebResourceRequest_DestroyHttpBodyStream(stream_);
541    }
542}
543
544void RawfileRequest::Start()
545{
546    OH_LOG_INFO(LOG_APP, "start a rawfile request.");
547    char* url;
548    OH_ArkWebResourceRequest_GetUrl(resourceRequest_, &url);
549    std::string urlStr(url);
550    std::size_t position = urlStr.rfind('/');
551    if (position != std::string::npos) {
552        rawfilePath_ = urlStr.substr(position + 1);
553    }
554    OH_ArkWeb_ReleaseString(url);
555
556    OH_ArkWeb_CreateResponse(&response_);
557    OH_ArkWebResourceRequest_GetHttpBodyStream(resourceRequest(), &stream_);
558    if (stream_) {
559        OH_LOG_ERROR(LOG_APP, "have http body stream");
560        OH_ArkWebHttpBodyStream_SetUserData(stream_, this);
561        OH_ArkWebHttpBodyStream_SetReadCallback(stream_, ReadCallback);
562        OH_ArkWebHttpBodyStream_Init(stream_, InitCallback);
563    } else {
564        thrd_t th;
565        if (thrd_create(&th, ReadRawfileOnWorkerThread, (void *)this) != thrd_success) {
566            OH_LOG_ERROR(LOG_APP, "create thread failed.");
567            return;
568        }
569
570        if (thrd_detach(th) != thrd_success) {
571            OH_LOG_ERROR(LOG_APP, "detach thread failed.");
572        }
573    }
574}
575
576// 在worker线程中读取rawfile,并通过ResourceHandler返回给Web内核。
577void RawfileRequest::ReadRawfileDataOnWorkerThread()
578{
579    OH_LOG_INFO(LOG_APP, "read rawfile in worker thread.");
580    const struct UrlInfo {
581        std::string resource;
582        std::string mimeType;
583    } urlInfos[] = {
584        {"test.html", "text/html"},
585        {"video.html", "text/html"},
586        {"isolated.html", "text/html"},
587        {"csp_bypassing.html", "text/html"},
588        {"post_data.html", "text/html"},
589        {"chunked_post_stream.html", "text/html"},
590        {"local.html", "text/html"},
591        {"service_worker.html", "text/html"},
592        {"csp_script.js", "text/javascript"},
593        {"sw.js", "text/javascript"},
594        {"isolated_script.js", "text/javascript"},
595        {"local_script.js", "text/javascript"},
596        {"test.mp4", "video/mp4"},
597        {"xhr", "application/json"}
598    };
599
600    if (!resourceManager()) {
601        OH_LOG_ERROR(LOG_APP, "read rawfile error, resource manager is nullptr.");
602        return;
603    }
604
605    RawFile *rawfile = OH_ResourceManager_OpenRawFile(resourceManager(), rawfilePath().c_str());
606    if (!rawfile) {
607        OH_ArkWebResponse_SetStatus(response(), 404);
608    } else {
609        OH_ArkWebResponse_SetStatus(response(), 200);
610    }
611
612    for (auto &urlInfo : urlInfos) {
613        if (urlInfo.resource == rawfilePath()) {
614            OH_ArkWebResponse_SetMimeType(response(), urlInfo.mimeType.c_str());
615            break;
616        }
617    }
618    OH_ArkWebResponse_SetCharset(response(), "UTF-8");
619
620    long len = OH_ResourceManager_GetRawFileSize(rawfile);
621    OH_ArkWebResponse_SetHeaderByName(response(), "content-length", std::to_string(len).c_str(), false);
622    DidReceiveResponse();
623
624    long consumed = 0;
625    uint8_t buffer[blockSize];
626    while (true) {
627        int ret = OH_ResourceManager_ReadRawFile(rawfile, buffer, blockSize);
628        OH_LOG_INFO(LOG_APP, "read rawfile %{public}d bytes.", ret);
629        if (ret == 0) {
630            break;
631        }
632        consumed += ret;
633        OH_ResourceManager_SeekRawFile(rawfile, consumed, 0);
634        DidReceiveData(buffer, ret);
635        memset(buffer, 0, blockSize);
636    }
637
638    OH_ResourceManager_CloseRawFile(rawfile);
639    DidFinish();
640}
641
642void RawfileRequest::Stop()
643{
644    OH_LOG_INFO(LOG_APP, "stop the rawfile request.");
645    std::lock_guard<std::mutex> guard(mutex_);
646    stopped_ = true;
647    if (response_) {
648        OH_ArkWeb_DestroyResponse(response_);
649    }
650    OH_ArkWebResourceRequest_Destroy(resourceRequest_);
651    OH_ArkWebResourceHandler_Destroy(resourceHandler_);
652}
653
654void RawfileRequest::DidReceiveResponse()
655{
656    OH_LOG_INFO(LOG_APP, "did receive response.");
657    std::lock_guard<std::mutex> guard(mutex_);
658    if (!stopped_) {
659        OH_ArkWebResourceHandler_DidReceiveResponse(resourceHandler_, response_);
660    }
661}
662
663void RawfileRequest::DidReceiveData(const uint8_t *buffer, int64_t bufLen)
664{
665    OH_LOG_INFO(LOG_APP, "did receive data.");
666    std::lock_guard<std::mutex> guard(mutex_);
667    if (!stopped_) {
668        OH_ArkWebResourceHandler_DidReceiveData(resourceHandler_, buffer, bufLen);
669    }
670}
671
672void RawfileRequest::DidFinish()
673{
674    OH_LOG_INFO(LOG_APP, "did finish.");
675    std::lock_guard<std::mutex> guard(mutex_);
676    if (!stopped_) {
677        OH_ArkWebResourceHandler_DidFinish(resourceHandler_);
678    }
679}
680
681void RawfileRequest::DidFailWithError(ArkWeb_NetError errorCode)
682{
683    OH_LOG_INFO(LOG_APP, "did finish with error %{public}d.", errorCode);
684    if (!stopped_) {
685        OH_ArkWebResourceHandler_DidFailWithError(resourceHandler_, errorCode);
686    }
687}
688
689void RawfileRequest::DidFailWithErrorV2(ArkWeb_NetError errorCode, bool completeIfNoResponse)
690{
691    OH_LOG_INFO(LOG_APP, "did finish with error %{public}d.", errorCode);
692    if (!stopped_) {
693        OH_ArkWebResourceHandler_DidFailWithErrorV2(resourceHandler_, errorCode, completeIfNoResponse);
694    }
695}
696```
697
698main/resources/rawfile/test.html
699```html
700<html>
701<head>
702<meta name="viewport" content="width=device-width,initial-scale=1">
703</head>
704
705<body>
706<h1> 网络拦截测试demo</h1>
707<a href="https://www.example.com/video.html">拦截视频资源请求,读取本地mp4文件</a><br/>
708<a href="https://www.example.com/csp_bypassing.html">测试三方协议忽略csp检查,并成功拦截</a><br/>
709<a href="https://www.example.com/isolated.html">测试拦截设置ISOLATED属性的三方协议</a><br/>
710<a href="https://www.example.com/local.html">测试拦截设置LOCAL属性的三方协议</a><br/>
711<a href="https://www.example.com/service_worker.html">测试拦截service worker触发的请求</a><br/>
712<a href="https://www.example.com/post_data.html">测试读取blob类型http body stream</a><br/>
713<a href="https://www.example.com/chunked_post_stream.html">测试读取chunked类型http body stream</a>
714</body>
715</html>
716```
717
718main/resources/rawfile/cat.svg
719```
720<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>
721```
722
723main/resources/rawfile/csp_bypassing.html
724```html
725<html>
726<head>
727<meta name="viewport" content="width=device-width,initial-scale=1">
728<meta http-equiv="Content-Security-Policy" content="default-src 'self'; media-src 'self'">
729</head>
730<body>
731<p>scheme: custom-csp-bypassing</p>
732<p>options: ARKWEB_SCHEME_OPTION_CSP_BYPASSING | ARKWEB_SCHEME_OPTION_STANDARD</p>
733<script src="custom-csp-bypassing://www.example.com/csp_script.js"></script>
734</body>
735</html>
736```
737
738main/resources/rawfile/csp_script.js
739```js
740const body = document.body;
741const element = document.createElement('div');
742element.textContent = 'csp_script.js bypass the csp rules';
743body.appendChild(element);
744```
745
746main/resources/rawfile/isolated_script.js
747```js
748const element = document.getElementById('isolated_test');
749element.textContent = 'isolated_script.js not blocked';
750```
751
752main/resources/rawfile/isolated.html
753```html
754<html>
755<head>
756<meta name="viewport" content="width=device-width,initial-scale=1">
757</head>
758<body>
759<p>scheme: custom-isolated</p>
760<p>options: ARKWEB_SCHEME_OPTION_DISPLAY_ISOLATED</p>
761<div id="isolated_test">isolated_script.js 被拦截</div>
762<script src="custom-isolated://www.example.com/isolated_script.js"></script>
763</body>
764</html>
765```
766
767main/resources/rawfile/local_script.js
768```js
769const element = document.getElementById('local_test');
770element.textContent = 'local_script.js not blocked.';
771```
772
773main/resources/rawfile/local.html
774```html
775<html>
776<head>
777<meta name="viewport" content="width=device-width,initial-scale=1">
778</head>
779<body>
780<p>scheme: custom-local</p>
781<p>options: ARKWEB_SCHEME_OPTION_LOCAL</p>
782<div id="local_test">local_script.js 被拦截</div>
783<script src="custom-local://www.example.com/local_script.js"></script>
784</body>
785</html>
786```
787
788main/resources/rawfile/post_data.html
789```html
790<html>
791<head>
792<meta name="viewport" content="width=device-width,initial-scale=1">
793<script>
794    function textPostXhr(url) {
795        var formData = new FormData();
796        var myBlob = new Blob(["This is my blob content"], {type : "text/plain"});
797        formData.append("upload", myBlob);
798        var xhr = new XMLHttpRequest();
799        xhr.open('POST', url, true);
800        xhr.send(formData);
801        xhr.onreadystatechange = function (err) {
802            console.log(err.target.status);
803        }
804    }
805    function textPutXhr(url) {
806        var formData = new FormData();
807        var myBlob = new Blob(["This is my blob content"], {type : "text/plain"});
808        formData.append("upload", myBlob);
809        var xhr = new XMLHttpRequest();
810        xhr.open('PUT', url, true);
811        xhr.send(formData);
812        xhr.onreadystatechange = function (err) {
813            console.log(err.target.status);
814        }
815    }
816</script>
817</head>
818<body>
819<div onclick="textPostXhr('https://www.example.com/xhr')">test xhr post</div>
820<div onclick="textPutXhr('https://www.example.com/xhr')">test xhr put</div>
821</body>
822</html>
823```
824
825main/resources/rawfile/service_worker.html
826```html
827<html>
828<head>
829<meta name="viewport" content="width=device-width,initial-scale=1">
830<script>
831function registerSuccess() {
832    const body = document.body;
833    const element = document.createElement('div');
834    element.textContent = 'register sw successful.';
835    body.appendChild(element);
836}
837
838navigator.serviceWorker.register('/sw.js')
839    .then(reg => registerSuccess())
840    .catch(error => console.log('failed!', error))
841</script>
842</head>
843<body>
844</body>
845</html>
846```
847
848main/resources/rawfile/sw.js
849```js
850self.addEventListener('install', event => {
851    console.log('v1 installing');
852    event.waitUntil(
853        caches.open('static-v1').then(cache => cache.add('/cat.svg'))
854    );
855});
856
857self.addEventListener('activate', event => {
858    console.log("v1 now ready to handle fetches.");
859});
860```
861
862main/resources/rawfile/video.html
863```html
864<html>
865<head>
866<meta name="viewport" content="width=device-width,initial-scale=1">
867</head>
868<body>
869<video width="400" height="400" controls>
870    <source src="https://www.example.com/test.mp4" type="video/mp4">
871</video>
872</body>
873</html>
874```
875
876main/resources/rawfile/chunked_post_stream.html
877```html
878<html>
879<head>
880<meta name="viewport" content="width=device-width,initial-scale=1">
881</head>
882<script>
883let uploaded = 0;
884let buf = new Uint8Array(1024 * 50);
885let start = Date.now();
886var rs = new ReadableStream({
887    pull(ctrl) {
888        uploaded += buf.byteLength;
889        crypto.getRandomValues(buf);
890        ctrl.enqueue(buf);
891        if ((start + 1000) < Date.now()) ctrl.close();
892    }
893});
894
895function test() {
896    fetch('https://www.example.com/xhr', {
897        method: 'POST',
898        body: rs,
899        duplex: 'half'
900    }).then(r => r.json()).then(console.log);
901}
902</script>
903<body>
904<div onclick="test()">test post chunked http body.</div>
905</body>
906</html>
907```
908
909main/resources/rawfile/xhr
910```
911{}
912```
913