<commit_before>'use strict';

import * as express from 'express';
import { ApiConfig } from '../config/api';
import { Proxy, HttpAgent } from '../config/proxy';
import { ProxyFilter } from './filter';
import { ProxyInterceptor } from './interceptor';
import { Logger } from '../logger';
import { AutoWired, Inject } from 'typescript-ioc';
import {getMilisecondsInterval} from '../utils/time-intervals';
import * as url from 'url';
import * as _ from 'lodash';
import * as getRawBody from 'raw-body';
const agentKeepAlive = require('agentkeepalive');
const httpProxy = require('../../lib/http-proxy');
const memoryStream = require('memory-streams').WritableStream;

/**
 * The API Proxy system. It uses [[http-proxy]](https://github.com/nodejitsu/node-http-proxy)
 * to proxy requests to a target API.
 */
@AutoWired
export class ApiProxy {
    @Inject private filter: ProxyFilter;
    @Inject private interceptor: ProxyInterceptor;
    @Inject private logger: Logger;

    /**
     * Configure a proxy for a given API
     */
    proxy(apiRouter: express.Router, api: ApiConfig) {
        if (api.proxy.parseReqBody) {
            apiRouter.use(this.configureBodyParser(api));
        }
        apiRouter.use(this.configureProxy(api));
    }

    configureProxyHeader(apiRouter: express.Router, api: ApiConfig) {
        if (!api.proxy.supressViaHeader) {
            const onHeaders = require('on-headers');
            const gatewayId = 'Tree-Gateway';
            apiRouter.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
                onHeaders(res, () => {
                    let viaHeader = res.get('Via');
                    if (viaHeader) {
                        viaHeader = viaHeader + ', ' + req.httpVersion + ' ' + gatewayId;
                    } else {
                        viaHeader = req.httpVersion + ' ' + gatewayId;
                    }
                    res.set('Via', viaHeader);
                });
                next();
            });
        }
    }

    private getHttpAgent(api: ApiConfig) {
        const apiAgentOptions: HttpAgent = api.proxy.httpAgent || {};
        const proxyTarget = url.parse(api.proxy.target.host);
        const agentOptions: any = {
            keepAlive: true
        };
        if (_.isBoolean(apiAgentOptions.keepAlive) && !apiAgentOptions.keepAlive) {
            agentOptions.keepAlive = false;
        }
        if (agentOptions.keepAlive) {
            agentOptions.keepAliveMsecs = getMilisecondsInterval(apiAgentOptions.keepAliveTime, 1000);
            agentOptions.freeSocketKeepAliveTimeout = getMilisecondsInterval(apiAgentOptions.freeSocketKeepAliveTimeout, 15000);
            agentOptions.maxFreeSockets = apiAgentOptions.maxFreeSockets || 256;
        }
        if (apiAgentOptions.maxSockets) {
            agentOptions.maxSockets = apiAgentOptions.maxSockets;
        }
        if (apiAgentOptions.timeout) {
            agentOptions.timeout = getMilisecondsInterval(apiAgentOptions.timeout);
        }

        if (proxyTarget.protocol && proxyTarget.protocol === 'https:') {
            return new agentKeepAlive.HttpsAgent(agentOptions);
        }
        return new agentKeepAlive(agentOptions);
    }

    private configureBodyParser(api: ApiConfig) {
        const limit = api.proxy.limit || '1mb';
        return (req: express.Request, res: express.Response, next: express.NextFunction) => {
            this.maybeParseBody(req, limit)
                .then(buf => {
                    req.body = buf;
                    next();
                })
                .catch(err => next(err));
        };
    }

    private configureProxy(api: ApiConfig) {
        const self = this;
        const apiProxy: Proxy = api.proxy;
        const proxyConfig: any = {
            changeOrigin: !apiProxy.preserveHostHdr,
            target: apiProxy.target.host
        };
        const httpAgent = self.getHttpAgent(api);
        if (httpAgent) {
            proxyConfig.agent = httpAgent;
        }
        if (apiProxy.timeout) {
            proxyConfig.proxyTimeout = getMilisecondsInterval(apiProxy.timeout);
        }

        const shouldWrapResponse = api.proxy.parseResBody && this.interceptor.hasResponseInterceptor(api.proxy);
        proxyConfig.delayHeaders = shouldWrapResponse;
        const proxy = httpProxy.createProxyServer(proxyConfig);

        const filter: (req: express.Request, res: express.Response) => boolean = this.getFilterMiddleware(api);
        this.handleRequestInterceptor(api, proxy);
        this.handleResponseInterceptor(api, proxy);

        return (req: express.Request, res: express.Response, next: express.NextFunction) => {
            if (shouldWrapResponse) {
                (<any>res).__write = res.write;
                (<any>res).__data = new memoryStream();
                res.write = (data: any, encoding?: any, cb?: any) => {
                    return (<any>res).__data.write(data, encoding, cb);<commit_msg>Proxy Error Handler<commit_after>'use strict';

import * as express from 'express';
import { ApiConfig } from '../config/api';
import { Proxy, HttpAgent } from '../config/proxy';
import { ProxyFilter } from './filter';
import { ProxyInterceptor } from './interceptor';
import { Logger } from '../logger';
import { AutoWired, Inject } from 'typescript-ioc';
import {getMilisecondsInterval} from '../utils/time-intervals';
import * as url from 'url';
import * as _ from 'lodash';
import * as getRawBody from 'raw-body';
const agentKeepAlive = require('agentkeepalive');
const httpProxy = require('../../lib/http-proxy');
const memoryStream = require('memory-streams').WritableStream;

/**
 * The API Proxy system. It uses [[http-proxy]](https://github.com/nodejitsu/node-http-proxy)
 * to proxy requests to a target API.
 */
@AutoWired
export class ApiProxy {
    @Inject private filter: ProxyFilter;
    @Inject private interceptor: ProxyInterceptor;
    @Inject private logger: Logger;

    /**
     * Configure a proxy for a given API
     */
    proxy(apiRouter: express.Router, api: ApiConfig) {
        if (api.proxy.parseReqBody) {
            apiRouter.use(this.configureBodyParser(api));
        }
        apiRouter.use(this.configureProxy(api));
    }

    configureProxyHeader(apiRouter: express.Router, api: ApiConfig) {
        if (!api.proxy.supressViaHeader) {
            const onHeaders = require('on-headers');
            const gatewayId = 'Tree-Gateway';
            apiRouter.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
                onHeaders(res, () => {
                    let viaHeader = res.get('Via');
                    if (viaHeader) {
                        viaHeader = viaHeader + ', ' + req.httpVersion + ' ' + gatewayId;
                    } else {
                        viaHeader = req.httpVersion + ' ' + gatewayId;
                    }
                    res.set('Via', viaHeader);
                });
                next();
            });
        }
    }

    private getHttpAgent(api: ApiConfig) {
        const apiAgentOptions: HttpAgent = api.proxy.httpAgent || {};
        const proxyTarget = url.parse(api.proxy.target.host);
        const agentOptions: any = {
            keepAlive: true
        };
        if (_.isBoolean(apiAgentOptions.keepAlive) && !apiAgentOptions.keepAlive) {
            agentOptions.keepAlive = false;
        }
        if (agentOptions.keepAlive) {
            agentOptions.keepAliveMsecs = getMilisecondsInterval(apiAgentOptions.keepAliveTime, 1000);
            agentOptions.freeSocketKeepAliveTimeout = getMilisecondsInterval(apiAgentOptions.freeSocketKeepAliveTimeout, 15000);
            agentOptions.maxFreeSockets = apiAgentOptions.maxFreeSockets || 256;
        }
        if (apiAgentOptions.maxSockets) {
            agentOptions.maxSockets = apiAgentOptions.maxSockets;
        }
        if (apiAgentOptions.timeout) {
            agentOptions.timeout = getMilisecondsInterval(apiAgentOptions.timeout);
        }

        if (proxyTarget.protocol && proxyTarget.protocol === 'https:') {
            return new agentKeepAlive.HttpsAgent(agentOptions);
        }
        return new agentKeepAlive(agentOptions);
    }

    private configureBodyParser(api: ApiConfig) {
        const limit = api.proxy.limit || '1mb';
        return (req: express.Request, res: express.Response, next: express.NextFunction) => {
            this.maybeParseBody(req, limit)
                .then(buf => {
                    req.body = buf;
                    next();
                })
                .catch(err => next(err));
        };
    }

    private configureProxy(api: ApiConfig) {
        const self = this;
        const apiProxy: Proxy = api.proxy;
        const proxyConfig: any = {
            changeOrigin: !apiProxy.preserveHostHdr,
            target: apiProxy.target.host
        };
        const httpAgent = self.getHttpAgent(api);
        if (httpAgent) {
            proxyConfig.agent = httpAgent;
        }
        if (apiProxy.timeout) {
            proxyConfig.proxyTimeout = getMilisecondsInterval(apiProxy.timeout);
        }

        const shouldWrapResponse = api.proxy.parseResBody && this.interceptor.hasResponseInterceptor(api.proxy);
        proxyConfig.delayHeaders = shouldWrapResponse;
        const proxy = httpProxy.createProxyServer(proxyConfig);
        proxy.on('error', (err: any, req: express.Request, res: express.Response) => {
            const hostname = (req.headers && req.headers.host) || (req.hostname || req.host);     // (websocket) || (node0.10 || node 4/5)
            const target = apiProxy.target.host ;
            const errReference = 'https://nodejs.org/api/errors.html#errors_common_system_errors'; // link to Node Common Systems Errors page

            this.logger.error('[Tree-Gateway] Error occurred while trying to proxy request %s from %s to %s (%s) (%s)', req.url, hostname, target, err.code, errReference);
        });

        const filter: (req: express.Request, res: express.Response) => boolean = this.getFilterMiddleware(api);
        this.handleRequestInterceptor(api, proxy);
        this.handleResponseInterceptor(api, proxy);

        return (req: express.Request, res: express.Response, next: express.NextFunction) => {
            if (shouldWrapResponse) {
                (<any>res).__write = res.write;
                (<any>res).__data = new memoryStream();
                res.write = (data: any, encoding?: any, cb?: any) => {
                    return (<any>res).__data.write(data, encoding, cb);
{'fraction_non_alphanumeric': 0.1192209511203555, 'fraction_numerical': 0.002930887775361634, 'mean_word_length': 2.312871907297213, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 26, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7086348', 'n_tokens_mistral': 2936, 'n_tokens_neox': 2862, 'n_words': 621}