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