This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-client-js.git


The following commit(s) were added to refs/heads/master by this push:
     new f85749f  feat: get response.body as a stream with the fetch API (#48)
f85749f is described below

commit f85749fbebbe9df167ef7a4bc9a95ab48a024056
Author: Qiuxia Fan <[email protected]>
AuthorDate: Wed Apr 14 20:52:18 2021 +0800

    feat: get response.body as a stream with the fetch API (#48)
---
 dist/licenses/LICENSES-fetch.txt              |  20 -----
 package-lock.json                             |   5 --
 package.json                                  |   3 +-
 src/interceptors/fetch.js                     |  21 -----
 src/interceptors/xhr.ts                       |  60 -------------
 src/trace/interceptors/fetch.ts               | 118 ++++++++++++++++++++++++
 src/trace/{segment.ts => interceptors/xhr.ts} |  78 ++++++++++------
 src/trace/segment.ts                          | 124 ++------------------------
 8 files changed, 175 insertions(+), 254 deletions(-)

diff --git a/dist/licenses/LICENSES-fetch.txt b/dist/licenses/LICENSES-fetch.txt
deleted file mode 100644
index 0e319d5..0000000
--- a/dist/licenses/LICENSES-fetch.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2014-2016 GitHub, Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/package-lock.json b/package-lock.json
index d02c275..ff58732 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7636,11 +7636,6 @@
       "integrity": 
"sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==",
       "dev": true
     },
-    "whatwg-fetch": {
-      "version": "3.5.0",
-      "resolved": 
"https://registry.npm.taobao.org/whatwg-fetch/download/whatwg-fetch-3.5.0.tgz";,
-      "integrity": "sha1-YFos0KcUbl2xQeKdHGKrhMDEyGg="
-    },
     "which": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz";,
diff --git a/package.json b/package.json
index 7aa409a..d1de945 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,6 @@
     "web-performance"
   ],
   "dependencies": {
-    "js-base64": "^3.6.0",
-    "whatwg-fetch": "^3.5.0"
+    "js-base64": "^3.6.0"
   }
 }
diff --git a/src/interceptors/fetch.js b/src/interceptors/fetch.js
deleted file mode 100644
index 62b9ea6..0000000
--- a/src/interceptors/fetch.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { fetch } from 'whatwg-fetch';
-export default function windowFetch() {
-  window.fetch = fetch;
-}
diff --git a/src/interceptors/xhr.ts b/src/interceptors/xhr.ts
deleted file mode 100644
index 5c55030..0000000
--- a/src/interceptors/xhr.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-export default function xhrInterceptor() {
-  const originalXHR = window.XMLHttpRequest as any;
-  const xhrSend = XMLHttpRequest.prototype.send;
-  const xhrOpen = XMLHttpRequest.prototype.open;
-
-  originalXHR.getRequestConfig = [];
-
-  function ajaxEventTrigger(event: string) {
-    const ajaxEvent = new CustomEvent(event, { detail: this });
-
-    window.dispatchEvent(ajaxEvent);
-  }
-
-  function customizedXHR() {
-    const liveXHR = new originalXHR();
-
-    liveXHR.addEventListener(
-      'readystatechange',
-      function () {
-        ajaxEventTrigger.call(this, 'xhrReadyStateChange');
-      },
-      false,
-    );
-
-    liveXHR.open = function (
-      method: string,
-      url: string,
-      async: boolean,
-      username?: string | null,
-      password?: string | null,
-    ) {
-      this.getRequestConfig = arguments;
-
-      return xhrOpen.apply(this, arguments);
-    };
-    liveXHR.send = function (body?: Document | BodyInit | null) {
-      return xhrSend.apply(this, arguments);
-    };
-
-    return liveXHR;
-  }
-  (window as any).XMLHttpRequest = customizedXHR;
-}
diff --git a/src/trace/interceptors/fetch.ts b/src/trace/interceptors/fetch.ts
new file mode 100644
index 0000000..5d946ed
--- /dev/null
+++ b/src/trace/interceptors/fetch.ts
@@ -0,0 +1,118 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { encode } from 'js-base64';
+import uuid from '../../services/uuid';
+import { SegmentFeilds, SpanFeilds } from '../type';
+import { CustomOptionsType } from '../../types';
+import { SpanLayer, SpanType, ComponentId, ServiceTag, ReportTypes } from 
'../../services/constant';
+
+export default function windowFetch(options: CustomOptionsType, segments: 
SegmentFeilds[]) {
+  const fetch: any = window.fetch;
+  let segment = {
+    traceId: '',
+    service: options.service + ServiceTag,
+    spans: [],
+    serviceInstance: options.serviceVersion,
+    traceSegmentId: '',
+  } as SegmentFeilds;
+  let url = {} as URL;
+
+  window.fetch = (...args) =>
+    (async (args: any) => {
+      const startTime = new Date().getTime();
+      const traceId = uuid();
+      const traceSegmentId = uuid();
+
+      if (args[0].startsWith('http://') || args[0].startsWith('https://')) {
+        url = new URL(args[0]);
+      } else if (args[0].startsWith('//')) {
+        url = new URL(`${window.location.protocol}${args[0]}`);
+      } else {
+        url = new URL(window.location.href);
+        url.pathname = args[0];
+      }
+
+      const noTrace = options.noTraceOrigins.some((rule: string | RegExp) => {
+        if (typeof rule === 'string') {
+          if (rule === url.origin) {
+            return true;
+          }
+        } else if (rule instanceof RegExp) {
+          if (rule.test(url.origin)) {
+            return true;
+          }
+        }
+      });
+      const hasTrace = !(
+        noTrace ||
+        (([ReportTypes.ERROR, ReportTypes.PERF, ReportTypes.SEGMENTS] as 
string[]).includes(url.pathname) &&
+          !options.traceSDKInternal)
+      );
+
+      if (hasTrace) {
+        const traceIdStr = String(encode(traceId));
+        const segmentId = String(encode(traceSegmentId));
+        const service = String(encode(segment.service));
+        const instance = String(encode(segment.serviceInstance));
+        const endpoint = String(encode(options.pagePath));
+        const peer = String(encode(url.host));
+        const index = segment.spans.length;
+        const values = 
`${1}-${traceIdStr}-${segmentId}-${index}-${service}-${instance}-${endpoint}-${peer}`;
+
+        args[1].headers['sw8'] = values;
+      }
+
+      const result = await fetch(...args);
+
+      if (hasTrace) {
+        const endTime = new Date().getTime();
+        const exitSpan: SpanFeilds = {
+          operationName: options.pagePath,
+          startTime: startTime,
+          endTime,
+          spanId: segment.spans.length,
+          spanLayer: SpanLayer,
+          spanType: SpanType,
+          isError: result.status === 0 || result.status >= 400 ? true : false, 
// when requests failed, the status is 0
+          parentSpanId: segment.spans.length - 1,
+          componentId: ComponentId,
+          peer: result.url.host,
+          tags: options.detailMode
+            ? [
+                {
+                  key: 'http.method',
+                  value: args[1].method,
+                },
+                {
+                  key: 'url',
+                  value: result.url,
+                },
+              ]
+            : undefined,
+        };
+        segment = {
+          ...segment,
+          traceId: traceId,
+          traceSegmentId: traceSegmentId,
+        };
+        segment.spans.push(exitSpan);
+        segments.push(segment);
+      }
+
+      return result;
+    })(args);
+}
diff --git a/src/trace/segment.ts b/src/trace/interceptors/xhr.ts
similarity index 77%
copy from src/trace/segment.ts
copy to src/trace/interceptors/xhr.ts
index 083fbb1..1274f51 100644
--- a/src/trace/segment.ts
+++ b/src/trace/interceptors/xhr.ts
@@ -14,21 +14,57 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+import { SpanLayer, SpanType, ReadyStatus, ComponentId, ServiceTag, 
ReportTypes } from '../../services/constant';
+import uuid from '../../services/uuid';
 import { encode } from 'js-base64';
-import xhrInterceptor from '../interceptors/xhr';
-import uuid from '../services/uuid';
-import Report from '../services/report';
-import { SegmentFeilds, SpanFeilds } from './type';
-import { SpanLayer, SpanType, ReadyStatus, ComponentId, ServiceTag, 
ReportTypes } from '../services/constant';
-import { CustomOptionsType } from '../types';
-import windowFetch from '../interceptors/fetch';
-
-export default function traceSegment(options: CustomOptionsType) {
-  let segments = [] as SegmentFeilds[];
+import { CustomOptionsType } from '../../types';
+import { SegmentFeilds, SpanFeilds } from '../type';
+
+export default function xhrInterceptor(options: CustomOptionsType, segments: 
SegmentFeilds[]) {
+  const originalXHR = window.XMLHttpRequest as any;
+  const xhrSend = XMLHttpRequest.prototype.send;
+  const xhrOpen = XMLHttpRequest.prototype.open;
+
+  originalXHR.getRequestConfig = [];
+
+  function ajaxEventTrigger(event: string) {
+    const ajaxEvent = new CustomEvent(event, { detail: this });
+
+    window.dispatchEvent(ajaxEvent);
+  }
+
+  function customizedXHR() {
+    const liveXHR = new originalXHR();
+
+    liveXHR.addEventListener(
+      'readystatechange',
+      function () {
+        ajaxEventTrigger.call(this, 'xhrReadyStateChange');
+      },
+      false,
+    );
+
+    liveXHR.open = function (
+      method: string,
+      url: string,
+      async: boolean,
+      username?: string | null,
+      password?: string | null,
+    ) {
+      this.getRequestConfig = arguments;
+
+      return xhrOpen.apply(this, arguments);
+    };
+    liveXHR.send = function (body?: Document | BodyInit | null) {
+      return xhrSend.apply(this, arguments);
+    };
+
+    return liveXHR;
+  }
+  (window as any).XMLHttpRequest = customizedXHR;
+
   const segCollector: { event: XMLHttpRequest; startTime: number; traceId: 
string; traceSegmentId: string }[] = [];
-  // inject interceptor
-  xhrInterceptor();
-  windowFetch();
+
   window.addEventListener('xhrReadyStateChange', (event: 
CustomEvent<XMLHttpRequest & { getRequestConfig: any[] }>) => {
     let segment = {
       traceId: '',
@@ -49,7 +85,7 @@ export default function traceSegment(options: 
CustomOptionsType) {
       url.pathname = config[1];
     }
 
-    const noTrace = options.noTraceOrigins.some((rule) => {
+    const noTrace = options.noTraceOrigins.some((rule: string | RegExp) => {
       if (typeof rule === 'string') {
         if (rule === url.origin) {
           return true;
@@ -139,18 +175,4 @@ export default function traceSegment(options: 
CustomOptionsType) {
       segments.push(segment);
     }
   });
-  window.onbeforeunload = function (e: Event) {
-    if (!segments.length) {
-      return;
-    }
-    new Report('SEGMENTS', options.collector).sendByXhr(segments);
-  };
-  //report per 5min
-  setInterval(() => {
-    if (!segments.length) {
-      return;
-    }
-    new Report('SEGMENTS', options.collector).sendByXhr(segments);
-    segments = [];
-  }, 300000);
 }
diff --git a/src/trace/segment.ts b/src/trace/segment.ts
index 083fbb1..e050daf 100644
--- a/src/trace/segment.ts
+++ b/src/trace/segment.ts
@@ -14,131 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { encode } from 'js-base64';
-import xhrInterceptor from '../interceptors/xhr';
-import uuid from '../services/uuid';
+
+import xhrInterceptor from './interceptors/xhr';
+import windowFetch from './interceptors/fetch';
 import Report from '../services/report';
-import { SegmentFeilds, SpanFeilds } from './type';
-import { SpanLayer, SpanType, ReadyStatus, ComponentId, ServiceTag, 
ReportTypes } from '../services/constant';
+import { SegmentFeilds } from './type';
 import { CustomOptionsType } from '../types';
-import windowFetch from '../interceptors/fetch';
 
 export default function traceSegment(options: CustomOptionsType) {
   let segments = [] as SegmentFeilds[];
-  const segCollector: { event: XMLHttpRequest; startTime: number; traceId: 
string; traceSegmentId: string }[] = [];
   // inject interceptor
-  xhrInterceptor();
-  windowFetch();
-  window.addEventListener('xhrReadyStateChange', (event: 
CustomEvent<XMLHttpRequest & { getRequestConfig: any[] }>) => {
-    let segment = {
-      traceId: '',
-      service: options.service + ServiceTag,
-      spans: [],
-      serviceInstance: options.serviceVersion,
-      traceSegmentId: '',
-    } as SegmentFeilds;
-    const xhrState = event.detail.readyState;
-    const config = event.detail.getRequestConfig;
-    let url = {} as URL;
-    if (config[1].startsWith('http://') || config[1].startsWith('https://')) {
-      url = new URL(config[1]);
-    } else if (config[1].startsWith('//')) {
-      url = new URL(`${window.location.protocol}${config[1]}`);
-    } else {
-      url = new URL(window.location.href);
-      url.pathname = config[1];
-    }
-
-    const noTrace = options.noTraceOrigins.some((rule) => {
-      if (typeof rule === 'string') {
-        if (rule === url.origin) {
-          return true;
-        }
-      } else if (rule instanceof RegExp) {
-        if (rule.test(url.origin)) {
-          return true;
-        }
-      }
-    });
-    if (noTrace) {
-      return;
-    }
-
-    if (
-      ([ReportTypes.ERROR, ReportTypes.PERF, ReportTypes.SEGMENTS] as 
string[]).includes(url.pathname) &&
-      !options.traceSDKInternal
-    ) {
-      return;
-    }
-
-    // The values of xhrState are from 
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState
-    if (xhrState === ReadyStatus.OPENED) {
-      const traceId = uuid();
-      const traceSegmentId = uuid();
-
-      segCollector.push({
-        event: event.detail,
-        startTime: new Date().getTime(),
-        traceId,
-        traceSegmentId,
-      });
-
-      const traceIdStr = String(encode(traceId));
-      const segmentId = String(encode(traceSegmentId));
-      const service = String(encode(segment.service));
-      const instance = String(encode(segment.serviceInstance));
-      const endpoint = String(encode(options.pagePath));
-      const peer = String(encode(url.host));
-      const index = segment.spans.length;
-      const values = 
`${1}-${traceIdStr}-${segmentId}-${index}-${service}-${instance}-${endpoint}-${peer}`;
+  xhrInterceptor(options, segments);
+  windowFetch(options, segments);
 
-      event.detail.setRequestHeader('sw8', values);
-    }
-
-    if (xhrState === ReadyStatus.DONE) {
-      const endTime = new Date().getTime();
-      for (let i = 0; i < segCollector.length; i++) {
-        if (segCollector[i].event.readyState === ReadyStatus.DONE) {
-          let url = {} as URL;
-          if (segCollector[i].event.status) {
-            url = new URL(segCollector[i].event.responseURL);
-          }
-          const exitSpan: SpanFeilds = {
-            operationName: options.pagePath,
-            startTime: segCollector[i].startTime,
-            endTime,
-            spanId: segment.spans.length,
-            spanLayer: SpanLayer,
-            spanType: SpanType,
-            isError: event.detail.status === 0 || event.detail.status >= 400 ? 
true : false, // when requests failed, the status is 0
-            parentSpanId: segment.spans.length - 1,
-            componentId: ComponentId,
-            peer: url.host,
-            tags: options.detailMode
-              ? [
-                  {
-                    key: 'http.method',
-                    value: config[0],
-                  },
-                  {
-                    key: 'url',
-                    value: segCollector[i].event.responseURL,
-                  },
-                ]
-              : undefined,
-          };
-          segment = {
-            ...segment,
-            traceId: segCollector[i].traceId,
-            traceSegmentId: segCollector[i].traceSegmentId,
-          };
-          segment.spans.push(exitSpan);
-          segCollector.splice(i, 1);
-        }
-      }
-      segments.push(segment);
-    }
-  });
   window.onbeforeunload = function (e: Event) {
     if (!segments.length) {
       return;

Reply via email to