Parse request bodies eagerly
Summary: Currently the network plugin was always storing the transfer format of our request/ response bodies: a base64 string. However, those are not searchable, and every formatter (and multiple can be invoked in a single view) was responsible for its own decompressing. This diff changes parsing requests / responses into an accurate format: a decompressed, de-base64-ed utf8 string, or a Uint8array for binary data. We will use this in the next diffs to do some more efficient searching Reviewed By: passy Differential Revision: D28200190 fbshipit-source-id: 33a71aeb7b340b58305e97fff4fa5ce472169b25
This commit is contained in:
committed by
Facebook GitHub Bot
parent
fc4a08eb55
commit
72e34bbd0d
@@ -26,27 +26,42 @@ export function getHeaderValue(
|
||||
return '';
|
||||
}
|
||||
|
||||
export function decodeBody(container: {
|
||||
headers?: Array<Header>;
|
||||
data: string | null | undefined;
|
||||
}): string {
|
||||
if (!container.data) {
|
||||
return '';
|
||||
export function isTextual(headers?: Array<Header>): boolean {
|
||||
const contentType = getHeaderValue(headers, 'Content-Type');
|
||||
if (!contentType) {
|
||||
return false;
|
||||
}
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
||||
return (
|
||||
contentType.startsWith('text/') ||
|
||||
contentType.startsWith('application/x-www-form-urlencoded') ||
|
||||
contentType.startsWith('application/json') ||
|
||||
contentType.startsWith('multipart/') ||
|
||||
contentType.startsWith('message/') ||
|
||||
contentType.startsWith('image/svg')
|
||||
);
|
||||
}
|
||||
|
||||
export function decodeBody(
|
||||
headers?: Array<Header>,
|
||||
data?: string | null,
|
||||
): string | undefined | Uint8Array {
|
||||
if (!data) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const isGzip =
|
||||
getHeaderValue(container.headers, 'Content-Encoding') === 'gzip';
|
||||
const isGzip = getHeaderValue(headers, 'Content-Encoding') === 'gzip';
|
||||
if (isGzip) {
|
||||
try {
|
||||
const binStr = Base64.atob(container.data);
|
||||
const dataArr = new Uint8Array(binStr.length);
|
||||
for (let i = 0; i < binStr.length; i++) {
|
||||
dataArr[i] = binStr.charCodeAt(i);
|
||||
}
|
||||
// The request is gzipped, so convert the base64 back to the raw bytes first,
|
||||
// then inflate. pako will detect the BOM headers and return a proper utf-8 string right away
|
||||
return pako.inflate(dataArr, {to: 'string'});
|
||||
// The request is gzipped, so convert the raw bytes back to base64 first.
|
||||
const dataArr = Base64.toUint8Array(data);
|
||||
// then inflate.
|
||||
return isTextual(headers)
|
||||
? // pako will detect the BOM headers and return a proper utf-8 string right away
|
||||
pako.inflate(dataArr, {to: 'string'})
|
||||
: pako.inflate(dataArr);
|
||||
} catch (e) {
|
||||
// on iOS, the stream send to flipper is already inflated, so the content-encoding will not
|
||||
// match the actual data anymore, and we should skip inflating.
|
||||
@@ -57,14 +72,18 @@ export function decodeBody(container: {
|
||||
}
|
||||
}
|
||||
// If this is not a gzipped request, assume we are interested in a proper utf-8 string.
|
||||
// - If the raw binary data in is needed, in base64 form, use container.data directly
|
||||
// - either directly use container.data (for example)
|
||||
return Base64.decode(container.data);
|
||||
// - If the raw binary data in is needed, in base64 form, use data directly
|
||||
// - either directly use data (for example)
|
||||
if (isTextual(headers)) {
|
||||
return Base64.decode(data);
|
||||
} else {
|
||||
return Base64.toUint8Array(data);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
`Flipper failed to decode request/response body (size: ${container.data.length}): ${e}`,
|
||||
`Flipper failed to decode request/response body (size: ${data.length}): ${e}`,
|
||||
);
|
||||
return '';
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,17 +97,31 @@ export function convertRequestToCurlCommand(
|
||||
const headerStr = `${header.key}: ${header.value}`;
|
||||
command += ` -H ${escapedString(headerStr)}`;
|
||||
});
|
||||
// Add body. TODO: we only want this for non-binary data! See D23403095
|
||||
const body = decodeBody({
|
||||
headers: request.requestHeaders,
|
||||
data: request.requestData,
|
||||
});
|
||||
if (body) {
|
||||
command += ` -d ${escapedString(body)}`;
|
||||
if (typeof request.requestData === 'string') {
|
||||
command += ` -d ${escapedString(request.requestData)}`;
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
export function bodyAsString(body: undefined | string | Uint8Array): string {
|
||||
if (body == undefined) {
|
||||
return '(empty)';
|
||||
}
|
||||
if (body instanceof Uint8Array) {
|
||||
return '(binary data)';
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
export function bodyAsBinary(
|
||||
body: undefined | string | Uint8Array,
|
||||
): Uint8Array | undefined {
|
||||
if (body instanceof Uint8Array) {
|
||||
return body;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function escapeCharacter(x: string) {
|
||||
const code = x.charCodeAt(0);
|
||||
return code < 16 ? '\\u0' + code.toString(16) : '\\u' + code.toString(16);
|
||||
@@ -167,21 +200,8 @@ export function requestsToText(requests: Request[]): string {
|
||||
.join('\n')}`;
|
||||
|
||||
// TODO: we want decoding only for non-binary data! See D23403095
|
||||
const requestData = request.requestData
|
||||
? decodeBody({
|
||||
headers: request.requestHeaders,
|
||||
data: request.requestData,
|
||||
})
|
||||
: null;
|
||||
const responseData = request.responseData
|
||||
? decodeBody({
|
||||
headers: request.responseHeaders,
|
||||
data: request.responseData,
|
||||
})
|
||||
: null;
|
||||
|
||||
if (requestData) {
|
||||
copyText += `\n\n${requestData}`;
|
||||
if (request.requestData) {
|
||||
copyText += `\n\n${request.requestData}`;
|
||||
}
|
||||
if (request.status) {
|
||||
copyText += `
|
||||
@@ -198,8 +218,8 @@ export function requestsToText(requests: Request[]): string {
|
||||
}`;
|
||||
}
|
||||
|
||||
if (responseData) {
|
||||
copyText += `\n\n${responseData}`;
|
||||
if (request.responseData) {
|
||||
copyText += `\n\n${request.responseData}`;
|
||||
}
|
||||
return copyText;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user