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