All files / src httpClient.ts

98.44% Statements 63/64
100% Branches 28/28
88.89% Functions 8/9
98.36% Lines 60/61

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153  1x     1x 1x         1x 1x 1x 1x 1x                                       1x             175x 175x 175x 175x 175x       1310x 5x     1310x 1x     1310x 1x     1310x 1307x   1310x     1310x 2x 2x           2x   2x 1x       1310x 1310x   1310x   1307x 1304x     2056x 2056x     1304x       1304x 319x     1304x 2x     1304x 1304x 52x     1304x 801x       1304x         1304x         1304x 1304x     1307x   1307x 236x 236x 236x 236x 236x 236x 236x               1071x        
// tslint:disable-next-line:no-var-requires
const HttpProxyAgent = require('http-proxy-agent');
 
import { HttpProxyAgentOptions } from "http-proxy-agent";
import fetch from "node-fetch";
import {
    Headers,
    RequestInit,
    Response,
} from "node-fetch";
import ClientError from "./error";
import RequestResponseLog from "./requestResponseLog";
import RequestResponseLogEntry, { RequestLogEntry, ResponseLogEntry } from "./requestResponseLogEntry";
import Logger from "./logger";
const log: Logger = new Logger();
 
export interface IRequestContext {
    "description"?: string;
}
 
export interface IProxy {
    "host": string;
    "port": string;
    "protocol": string;
    "secureProxy": boolean;
    "proxyAuthorizationHeader"?: string;
}
 
export interface IHttpClientOptions {
    "authorizationHeader"?: string;
    "logRequestResponse"?: boolean;
    "proxy"?: IProxy;
    "origin"?: string;
}
export class HttpClient {
    private proxy?: IProxy;
    private authorizationHeader?: string;
    private logRequestResponse: boolean;
    private origin: string;
 
    public constructor(options: IHttpClientOptions) {
        log.debug("constructor");
        this.authorizationHeader = options.authorizationHeader;
        this.proxy = options.proxy;
        this.logRequestResponse = options.logRequestResponse || false;
        this.origin = options.origin || "";
    }
    public async getHttpResponse(url: string, requestInit: RequestInit, expectedHttpStatusCode: number[], context: IRequestContext): Promise<Response> {
 
        if (!requestInit.headers) {
            requestInit.headers = new Headers();
        }
 
        if (!requestInit.method) {
            requestInit.method = "UNDEFINED";
        }
 
        if (!context.description) {
            context.description = "";
        }
 
        if (this.authorizationHeader) {
            (requestInit.headers as Headers).append("Authorization", this.authorizationHeader);
        }
        (requestInit.headers as Headers).append("User-Agent", "nextcloud-node-client");
 
        // set the proxy
        if (this.proxy) {
            log.debug("proxy agent used");
            const options: HttpProxyAgentOptions = {
                host: this.proxy.host,
                port: this.proxy.port,
                protocol: this.proxy.protocol,
            };
 
            requestInit.agent = new HttpProxyAgent(options);
 
            if (this.proxy.proxyAuthorizationHeader) {
                (requestInit.headers as Headers).append("Proxy-Authorization", this.proxy.proxyAuthorizationHeader);
            }
        }
 
        log.debug("getHttpResponse request header:", requestInit.headers);
        log.debug("getHttpResponse url", url, requestInit);
 
        const response: Response = await fetch(url, requestInit);
 
        if (this.logRequestResponse) {
            const responseText = await response.text();
 
            // overwrite response functions as the body uses a stearm object...
            response.text = async () => {
                return responseText;
            };
 
            response.body.pipe = (destination: NodeJS.WritableStream, options?: { end?: boolean; }): any => {
                destination.write(responseText);
            };
 
            response.json = async () => {
                return JSON.parse(responseText);
            };
 
            response.buffer = async () => {
                return Buffer.from(responseText);
            };
 
            let body: string = `<Body is ${typeof requestInit.body}>`;
            if (requestInit.body && requestInit.body instanceof Buffer) {
                body = `<Body is Buffer ${requestInit.body.length}>`;
            }
 
            if (typeof requestInit.body === "string") {
                body = requestInit.body;
            }
 
            const reqLogEntry: RequestLogEntry =
                new RequestLogEntry(url.replace(this.origin, ""),
                    requestInit.method, context.description,
                    body);
 
            const resLogEntry: ResponseLogEntry =
                new ResponseLogEntry(response.status,
                    await response.text(),
                    response.headers.get("Content-Type") as string,
                    response.headers.get("Content-Location") || "");
 
            const rrLog: RequestResponseLog = RequestResponseLog.getInstance();
            await rrLog.addEntry(new RequestResponseLogEntry(reqLogEntry, resLogEntry));
        }
 
        const responseContentType: string | null = response.headers.get("content-type");
 
        if (expectedHttpStatusCode.indexOf(response.status) === -1) {
            log.debug("getHttpResponse unexpected status response "+ response.status + " " + response.statusText);
            log.debug("getHttpResponse description "+ context.description);
            log.debug("getHttpResponse expected "+ expectedHttpStatusCode.join(","));
            log.debug("getHttpResponse headers ", JSON.stringify(response.headers, null, 4));
            log.debug("getHttpResponse request body ", requestInit.body);
            log.debug("getHttpResponse text:", await response.text());
            throw new ClientError(`HTTP response status ${response.status} not expected. Expected status: ${expectedHttpStatusCode.join(",")} - status text: ${response.statusText}`,
                "ERR_UNEXPECTED_HTTP_STATUS",
                {
                    expectedHttpStatusCodes: expectedHttpStatusCode,
                    responseStatus: response.status,
                    responseStatusText: response.statusText,
                });
        }
        return response;
    }
 
}