mirror of
https://github.com/dream-num/univer.git
synced 2026-09-24 15:32:18 +08:00
chore: add network logging (#4327)
This commit is contained in:
@@ -21,10 +21,12 @@ import type { Subscriber } from 'rxjs';
|
||||
import type { HTTPRequest } from '../request';
|
||||
import type { HTTPEvent, HTTPResponseBody } from '../response';
|
||||
import type { IHTTPImplementation } from './implementation';
|
||||
import { ILogService } from '@univerjs/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { HTTPHeaders } from '../headers';
|
||||
import { HTTPStatusCode } from '../http';
|
||||
import { HTTPProgress, HTTPResponse, HTTPResponseError } from '../response';
|
||||
import { parseFetchParamsFromRequest } from './util';
|
||||
|
||||
/**
|
||||
* An HTTP implementation using Fetch API. This implementation can both run in browser and Node.js.
|
||||
@@ -32,10 +34,14 @@ import { HTTPProgress, HTTPResponse, HTTPResponseError } from '../response';
|
||||
* It does not support streaming response yet (May 12, 2024).
|
||||
*/
|
||||
export class FetchHTTPImplementation implements IHTTPImplementation {
|
||||
constructor(
|
||||
@ILogService private readonly _logService: ILogService
|
||||
) { }
|
||||
|
||||
send(request: HTTPRequest): Observable<HTTPEvent<any>> {
|
||||
return new Observable((subscriber) => {
|
||||
const abortController = new AbortController();
|
||||
this._send(request, subscriber, abortController).then(() => {}, (error) => {
|
||||
this._send(request, subscriber, abortController).catch((error) => {
|
||||
subscriber.error(new HTTPResponseError({
|
||||
error,
|
||||
request,
|
||||
@@ -50,21 +56,28 @@ export class FetchHTTPImplementation implements IHTTPImplementation {
|
||||
let response: Response;
|
||||
|
||||
try {
|
||||
const fetchParams = this._parseFetchParamsFromRequest(request);
|
||||
const fetchPromise = fetch(request.getUrlWithParams(), {
|
||||
const fetchParams = parseFetchParamsFromRequest(request);
|
||||
const urlWithParams = request.getUrlWithParams();
|
||||
const fetchPromise = fetch(urlWithParams, {
|
||||
signal: abortController.signal,
|
||||
...fetchParams,
|
||||
});
|
||||
|
||||
this._logService.debug(`[FetchHTTPImplementation]: sending request to url ${urlWithParams} with params ${fetchParams}`);
|
||||
|
||||
response = await fetchPromise;
|
||||
} catch (error: any) {
|
||||
subscriber.error(new HTTPResponseError({
|
||||
const e = new HTTPResponseError({
|
||||
request,
|
||||
error,
|
||||
status: error.status ?? 0,
|
||||
statusText: error.statusText ?? 'Unknown Error',
|
||||
headers: error.headers,
|
||||
}));
|
||||
});
|
||||
|
||||
this._logService.error('[FetchHTTPImplementation]: network error', e);
|
||||
|
||||
subscriber.error(e);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -87,13 +100,17 @@ export class FetchHTTPImplementation implements IHTTPImplementation {
|
||||
statusText,
|
||||
}));
|
||||
} else {
|
||||
subscriber.error(new HTTPResponseError({
|
||||
const e = new HTTPResponseError({
|
||||
request,
|
||||
error: body,
|
||||
status,
|
||||
statusText,
|
||||
headers: responseHeaders,
|
||||
}));
|
||||
});
|
||||
|
||||
this._logService.error('[FetchHTTPImplementation]: network error', e);
|
||||
|
||||
subscriber.error(e);
|
||||
}
|
||||
|
||||
subscriber.complete();
|
||||
@@ -138,27 +155,21 @@ export class FetchHTTPImplementation implements IHTTPImplementation {
|
||||
const body = deserialize(request, all, contentType);
|
||||
return body;
|
||||
} catch (error) {
|
||||
subscriber.error(new HTTPResponseError({
|
||||
const e = new HTTPResponseError({
|
||||
request,
|
||||
error,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: new HTTPHeaders(response.headers),
|
||||
}));
|
||||
});
|
||||
|
||||
this._logService.error('[FetchHTTPImplementation]: network error', e);
|
||||
|
||||
subscriber.error(e);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private _parseFetchParamsFromRequest(request: HTTPRequest): RequestInit {
|
||||
const fetchParams: RequestInit = {
|
||||
method: request.method,
|
||||
headers: request.getHeadersInit(),
|
||||
body: request.getBody(),
|
||||
credentials: request.withCredentials ? 'include' : undefined,
|
||||
};
|
||||
|
||||
return fetchParams;
|
||||
}
|
||||
}
|
||||
|
||||
function mergeChunks(chunks: Uint8Array[], totalLength: number): Uint8Array {
|
||||
|
||||
@@ -14,11 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createIdentifier } from '@univerjs/core';
|
||||
import type { Observable } from 'rxjs';
|
||||
|
||||
import type { HTTPRequest } from '../request';
|
||||
import type { HTTPEvent } from '../response';
|
||||
import { createIdentifier } from '@univerjs/core';
|
||||
|
||||
/**
|
||||
* HTTP service could be implemented differently on platforms.
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* Licensed 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 type { HTTPRequest } from '../request';
|
||||
|
||||
export function parseFetchParamsFromRequest(request: HTTPRequest): RequestInit {
|
||||
const fetchParams: RequestInit = {
|
||||
method: request.method,
|
||||
headers: request.getHeadersInit(),
|
||||
body: request.getBody(),
|
||||
credentials: request.withCredentials ? 'include' : undefined,
|
||||
};
|
||||
|
||||
return fetchParams;
|
||||
}
|
||||
|
||||
@@ -17,38 +17,38 @@
|
||||
/* eslint-disable max-lines-per-function */
|
||||
/* eslint-disable ts/no-explicit-any */
|
||||
|
||||
import type { Nullable } from '@univerjs/core';
|
||||
import type { Observer } from 'rxjs';
|
||||
import type { HTTPRequest } from '../request';
|
||||
|
||||
import type { HTTPEvent } from '../response';
|
||||
import type { IHTTPImplementation } from './implementation';
|
||||
import { ILogService, type Nullable } from '@univerjs/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { HTTPHeaders } from '../headers';
|
||||
import { ErrorStatusCodeLowerBound, HTTPStatusCode, SuccessStatusCodeLowerBound } from '../http';
|
||||
import { HTTPResponse, HTTPResponseError, ResponseHeader } from '../response';
|
||||
import { parseFetchParamsFromRequest } from './util';
|
||||
|
||||
/**
|
||||
* An HTTP implementation using XHR. HTTP service provided by this service could only be async (we do not support sync XHR now).
|
||||
*/
|
||||
export class XHRHTTPImplementation implements IHTTPImplementation {
|
||||
constructor(
|
||||
@ILogService private readonly _logService: ILogService
|
||||
) { }
|
||||
|
||||
send(request: HTTPRequest): Observable<HTTPEvent<any>> {
|
||||
return new Observable((observer: Observer<HTTPEvent<any>>) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open(request.method, request.getUrlWithParams());
|
||||
const urlWithParams = request.getUrlWithParams();
|
||||
const fetchParams = parseFetchParamsFromRequest(request);
|
||||
|
||||
xhr.open(request.method, urlWithParams);
|
||||
if (request.withCredentials) {
|
||||
xhr.withCredentials = true;
|
||||
}
|
||||
|
||||
// set default HTTP headers
|
||||
|
||||
request.headers.forEach((key, value) => xhr.setRequestHeader(key, value.join(',')));
|
||||
if (!request.headers.has('Accept')) {
|
||||
xhr.setRequestHeader('Accept', 'application/json, text/plain, */*');
|
||||
}
|
||||
|
||||
if (!request.headers.has('Content-Type')) {
|
||||
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
|
||||
if (fetchParams.headers) {
|
||||
Object.entries(fetchParams.headers).forEach(([key, value]) => xhr.setRequestHeader(key, value));
|
||||
}
|
||||
|
||||
let responseHeader: Nullable<ResponseHeader>;
|
||||
@@ -103,21 +103,22 @@ export class XHRHTTPImplementation implements IHTTPImplementation {
|
||||
})
|
||||
);
|
||||
} else {
|
||||
observer.error(
|
||||
new HTTPResponseError({
|
||||
request,
|
||||
error,
|
||||
headers,
|
||||
status,
|
||||
statusText,
|
||||
})
|
||||
);
|
||||
// Handler server logic error here
|
||||
const e = new HTTPResponseError({
|
||||
request,
|
||||
error,
|
||||
headers,
|
||||
status,
|
||||
statusText,
|
||||
});
|
||||
|
||||
this._logService.error('[XHRHTTPImplementation]: network error', e);
|
||||
|
||||
observer.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
const onErrorHandler = (error: ProgressEvent) => {
|
||||
const res = new HTTPResponseError({
|
||||
const e = new HTTPResponseError({
|
||||
request,
|
||||
error,
|
||||
status: xhr.status || 0,
|
||||
@@ -125,7 +126,9 @@ export class XHRHTTPImplementation implements IHTTPImplementation {
|
||||
headers: buildResponseHeader().headers,
|
||||
});
|
||||
|
||||
observer.error(res);
|
||||
this._logService.error('[XHRHTTPImplementation]: network error', e);
|
||||
|
||||
observer.error(e);
|
||||
};
|
||||
|
||||
xhr.addEventListener('load', onLoadHandler);
|
||||
@@ -136,6 +139,8 @@ export class XHRHTTPImplementation implements IHTTPImplementation {
|
||||
const body = request.getBody();
|
||||
xhr.send(body);
|
||||
|
||||
this._logService.debug(`[XHRHTTPImplementation]: sending request to url ${urlWithParams} with params ${fetchParams}`);
|
||||
|
||||
// Abort the request if the subscription is disposed before the request completes.
|
||||
return () => {
|
||||
if (xhr.readyState !== xhr.DONE) {
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
|
||||
import type { HTTPHeaders } from './headers';
|
||||
import { ApplicationJSONType } from './headers';
|
||||
import type { HTTPResponseType } from './http';
|
||||
import type { HTTPParams } from './params';
|
||||
import { ApplicationJSONType } from './headers';
|
||||
|
||||
export type HTTPRequestMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
||||
|
||||
@@ -51,7 +51,7 @@ export class HTTPRequest {
|
||||
readonly method: HTTPRequestMethod,
|
||||
readonly url: string,
|
||||
readonly requestParams?: IHTTPRequestParams
|
||||
) {}
|
||||
) { }
|
||||
|
||||
getUrlWithParams(): string {
|
||||
const params = this.requestParams?.params?.toString();
|
||||
|
||||
Reference in New Issue
Block a user