diff --git a/desktop/plugins/network/RequestDetails.tsx b/desktop/plugins/network/RequestDetails.tsx index 0c2e5e78b..e48e5e1e5 100644 --- a/desktop/plugins/network/RequestDetails.tsx +++ b/desktop/plugins/network/RequestDetails.tsx @@ -19,6 +19,7 @@ import { Select, styled, colors, + SmallText, } from 'flipper'; import {decodeBody, getHeaderValue} from './utils'; import {formatBytes} from './index'; @@ -332,14 +333,23 @@ class RequestBodyInspector extends Component<{ }> { render() { const {request, formattedText} = this.props; + if (request.data == null || request.data.trim() === '') { + return ; + } const bodyFormatters = formattedText ? TextBodyFormatters : BodyFormatters; - let component; for (const formatter of bodyFormatters) { if (formatter.formatRequest) { try { - component = formatter.formatRequest(request); + const component = formatter.formatRequest(request); if (component) { - break; + return ( + + {component} + + Formatted by {formatter.constructor.name} + + + ); } } catch (e) { console.warn( @@ -349,10 +359,7 @@ class RequestBodyInspector extends Component<{ } } } - - component = component || {decodeBody(request)}; - - return {component}; + return renderRawBody(request); } } @@ -363,14 +370,23 @@ class ResponseBodyInspector extends Component<{ }> { render() { const {request, response, formattedText} = this.props; + if (response.data == null || response.data.trim() === '') { + return ; + } const bodyFormatters = formattedText ? TextBodyFormatters : BodyFormatters; - let component; for (const formatter of bodyFormatters) { if (formatter.formatResponse) { try { - component = formatter.formatResponse(request, response); + const component = formatter.formatResponse(request, response); if (component) { - break; + return ( + + {component} + + Formatted by {formatter.constructor.name} + + + ); } } catch (e) { console.warn( @@ -380,13 +396,43 @@ class ResponseBodyInspector extends Component<{ } } } - - component = component || {decodeBody(response)}; - - return {component}; + return renderRawBody(response); } } +const FormattedBy = styled(SmallText)({ + marginTop: 8, + fontSize: '0.7em', + textAlign: 'center', + display: 'block', +}); + +const Empty = () => ( + + (empty) + +); + +function renderRawBody(container: Request | Response) { + const decoded = decodeBody(container); + return ( + + {decoded ? ( + + {decoded} + + ) : ( + <> + (Failed to decode) + + {container.data} + + + )} + + ); +} + const MediaContainer = styled(FlexColumn)({ alignItems: 'center', justifyContent: 'center', @@ -662,7 +708,11 @@ class GraphQLFormatter { }; formatRequest = (request: Request) => { if (request.url.indexOf('graphql') > 0) { - const data = querystring.parse(decodeBody(request)); + const decoded = decodeBody(request); + if (!decoded) { + return undefined; + } + const data = querystring.parse(decoded); if (typeof data.variables === 'string') { data.variables = JSON.parse(data.variables); } @@ -725,16 +775,40 @@ class FormUrlencodedFormatter { formatRequest = (request: Request) => { const contentType = getHeaderValue(request.headers, 'content-type'); if (contentType.startsWith('application/x-www-form-urlencoded')) { + const decoded = decodeBody(request); + if (!decoded) { + return undefined; + } return ( ); } }; } +class BinaryFormatter { + formatRequest(request: Request) { + return this.format(request); + } + + formatResponse(_request: Request, response: Response) { + return this.format(response); + } + + format(container: Request | Response) { + if ( + getHeaderValue(container.headers, 'content-type') === + 'application/octet-stream' + ) { + return '(binary data)'; // we could offer a download button here? + } + return undefined; + } +} + const BodyFormatters: Array = [ new ImageFormatter(), new VideoFormatter(), @@ -744,6 +818,7 @@ const BodyFormatters: Array = [ new JSONFormatter(), new FormUrlencodedFormatter(), new XMLTextFormatter(), + new BinaryFormatter(), ]; const TextBodyFormatters: Array = [new JSONTextFormatter()]; diff --git a/desktop/plugins/network/utils.tsx b/desktop/plugins/network/utils.tsx index 0de72428e..a5cd2c64d 100644 --- a/desktop/plugins/network/utils.tsx +++ b/desktop/plugins/network/utils.tsx @@ -33,7 +33,9 @@ export function decodeBody(container: Request | Response): string { return b64Decoded; } catch (e) { - console.warn('Discarding malformed body, size: ' + b64Decoded.length); + console.warn( + `Flipper failed to decode request/response body (size: ${b64Decoded.length}): ${e}`, + ); return ''; } } @@ -52,6 +54,9 @@ function decompress(body: string): string { } catch (e) { // Sometimes Content-Encoding is 'gzip' but the body is already decompressed. // Assume this is the case when decompression fails. + if (!('' + e).includes('incorrect header check')) { + console.warn('decompression failed: ' + e); + } } return body;