Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 0 additions & 210 deletions src/utils/api-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {deepCopy} from './deep-copy';
import {FirebaseApp} from '../firebase-app';
import {AppErrorCodes, FirebaseAppError} from './error';
import * as validator from './validator';
import {OutgoingHttpHeaders} from 'http';

import http = require('http');
import https = require('https');
Expand Down Expand Up @@ -342,215 +341,6 @@ export class AuthorizedHttpClient extends HttpClient {
}
}

/**
* Base class for handling HTTP requests.
*/
export class HttpRequestHandler {
/**
* Sends HTTP requests and returns a promise that resolves with the result.
* Will retry once if the first attempt encounters an AppErrorCodes.NETWORK_ERROR.
*
* @param {string} host The HTTP host.
* @param {number} port The port number.
* @param {string} path The endpoint path.
* @param {HttpMethod} httpMethod The http method.
* @param {object} [data] The request JSON.
* @param {object} [headers] The request headers.
* @param {number} [timeout] The request timeout in milliseconds.
* @return {Promise<object>} A promise that resolves with the response.
*/
public sendRequest(
host: string,
port: number,
path: string,
httpMethod: HttpMethod,
data?: object,
headers?: object,
timeout?: number): Promise<object> {
// Convenience for calling the real _sendRequest() method with the original params.
const sendOneRequest = () => {
return this._sendRequest(host, port, path, httpMethod, data, headers, timeout);
};

return sendOneRequest()
.catch ((response: { statusCode: number, error: string | object }) => {
// Retry if the request failed due to a network error.
if (response.error instanceof FirebaseAppError) {
if ((response.error as FirebaseAppError).hasCode(AppErrorCodes.NETWORK_ERROR)) {
return sendOneRequest();
}
}
return Promise.reject(response);
});
}

/**
* Sends HTTP requests and returns a promise that resolves with the result.
*
* @param {string} host The HTTP host.
* @param {number} port The port number.
* @param {string} path The endpoint path.
* @param {HttpMethod} httpMethod The http method.
* @param {object} [data] The request JSON.
* @param {object} [headers] The request headers.
* @param {number} [timeout] The request timeout in milliseconds.
* @return {Promise<object>} A promise that resolves with the response.
*/
private _sendRequest(
host: string,
port: number,
path: string,
httpMethod: HttpMethod,
data?: object,
headers?: object,
timeout?: number): Promise<any> {
let requestData;
if (data) {
try {
requestData = JSON.stringify(data);
} catch (e) {
return Promise.reject(e);
}
}
const options: https.RequestOptions = {
method: httpMethod,
host,
port,
path,
headers: headers as OutgoingHttpHeaders,
};
// Only https endpoints.
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
const buffers: Buffer[] = [];
res.on('data', (buffer: Buffer) => buffers.push(buffer));
res.on('end', () => {
const response = Buffer.concat(buffers).toString();

const statusCode = res.statusCode || 200;

const responseHeaders = res.headers || {};
const contentType = responseHeaders['content-type'] || 'application/json';

if (contentType.indexOf('text/html') !== -1 || contentType.indexOf('text/plain') !== -1) {
// Text response
if (statusCode >= 200 && statusCode < 300) {
resolve(response);
} else {
reject({
statusCode,
error: response,
});
}
} else {
// JSON response
try {
const json = JSON.parse(response);
if (statusCode >= 200 && statusCode < 300) {
resolve(json);
} else {
reject({
statusCode,
error: json,
});
}
} catch (error) {
const parsingError = new FirebaseAppError(
AppErrorCodes.UNABLE_TO_PARSE_RESPONSE,
`Failed to parse response data: "${ error.toString() }". Raw server` +
`response: "${ response }". Status code: "${ res.statusCode }". Outgoing ` +
`request: "${ options.method } ${options.host}${ options.path }"`,
);
reject({
statusCode,
error: parsingError,
});
}
}
});
});

if (timeout) {
// Listen to timeouts and throw a network error.
req.on('socket', (socket) => {
socket.setTimeout(timeout);
socket.on('timeout', () => {
req.abort();

const networkTimeoutError = new FirebaseAppError(
AppErrorCodes.NETWORK_TIMEOUT,
`${ host } network timeout. Please try again.`,
);
reject({
statusCode: 408,
error: networkTimeoutError,
});
});
});
}

req.on('error', (error) => {
const networkRequestError = new FirebaseAppError(
AppErrorCodes.NETWORK_ERROR,
`A network request error has occurred: ${ error && error.message }`,
);
reject({
statusCode: 502,
error: networkRequestError,
});
});

if (requestData) {
req.write(requestData);
}

req.end();
});
}
}

/**
* Class that extends HttpRequestHandler and signs HTTP requests with a service
* credential access token.
*
* @param {Credential} credential The service account credential used to
* sign HTTP requests.
* @constructor
*/
export class SignedApiRequestHandler extends HttpRequestHandler {
constructor(private app_: FirebaseApp) {
super();
}

/**
* Sends HTTP requests and returns a promise that resolves with the result.
*
* @param {string} host The HTTP host.
* @param {number} port The port number.
* @param {string} path The endpoint path.
* @param {HttpMethod} httpMethod The http method.
* @param {object} data The request JSON.
* @param {object} headers The request headers.
* @param {number} timeout The request timeout in milliseconds.
* @return {Promise} A promise that resolves with the response.
*/
public sendRequest(
host: string,
port: number,
path: string,
httpMethod: HttpMethod,
data?: object,
headers?: object,
timeout?: number): Promise<object> {
return this.app_.INTERNAL.getToken().then((accessTokenObj) => {
const headersCopy: object = (headers && deepCopy(headers)) || {};
const authorizationHeaderKey = 'Authorization';
headersCopy[authorizationHeaderKey] = 'Bearer ' + accessTokenObj.accessToken;
return super.sendRequest(host, port, path, httpMethod, data, headersCopy, timeout);
});
}
}

/**
* Class that defines all the settings for the backend API endpoint.
*
Expand Down
Loading