microsoft/vscode-react-native

Public

mirrored from https://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.11.2

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/extension/networkInspector/requestBodyFormatters/requestBodyFormatter.ts

145lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import { Base64 } from "js-base64";
5import * as pako from "pako";
6import { Request, Response, Header } from "../networkMessageData";
7import { OutputChannelLogger } from "../../log/OutputChannelLogger";
8import { notNullOrUndefined } from "../../../common/utils";
9import { JSONFormatter } from "./jsonFormatter";
10import { ImageFormatter } from "./imageFormatter";
11import { GraphQLFormatter } from "./graphQLFormatter";
12import { FormUrlencodedFormatter } from "./formUrlencodedFormatter";
13
14export type FormattedBody = string | Record<string, any> | Array<Record<string, any>>;
15
16export interface IFormatter {
17 formatRequest?: (request: Request, contentType: string) => FormattedBody | null;
18 formatResponse?: (response: Response, contentType: string) => FormattedBody | null;
19}
20
21export class RequestBodyFormatter {
22 protected logger: OutputChannelLogger;
23 private formatters: Array<IFormatter>;
24
25 constructor(logger: OutputChannelLogger) {
26 this.logger = logger;
27 this.formatters = [
28 new ImageFormatter(),
29 new GraphQLFormatter(this.logger),
30 new JSONFormatter(this.logger),
31 new FormUrlencodedFormatter(this.logger),
32 ];
33 }
34
35 public formatBody(container: Request | Response): FormattedBody {
36 const contentType = getHeaderValue(container.headers, "content-type");
37
38 for (const formatter of this.formatters) {
39 try {
40 let formattedRes = null;
41 // if container is a response
42 if ((<any>container).status) {
43 if (formatter.formatResponse) {
44 formattedRes = formatter.formatResponse(<Response>container, contentType);
45 }
46 } else if (formatter.formatRequest) {
47 formattedRes = formatter.formatRequest(<Request>container, contentType);
48 }
49
50 if (notNullOrUndefined(formattedRes)) {
51 return formattedRes;
52 }
53 } catch (err) {
54 this.logger.debug(
55 `RequestBodyFormatter exception from ${formatter.constructor.name} ${String(
56 err.message,
57 )}`,
58 );
59 }
60 }
61
62 return decodeBody(container, this.logger);
63 }
64}
65
66/**
67 * @preserve
68 * Start region: the code is borrowed from https://github.com/facebook/flipper/blob/v0.79.1/desktop/plugins/network/utils.tsx#L23-L60
69 *
70 * Copyright (c) Facebook, Inc. and its affiliates.
71 *
72 * This source code is licensed under the MIT license found in the
73 * LICENSE file in the root directory of this source tree.
74 *
75 * @format
76 */
77export function decodeBody(container: Request | Response, logger?: OutputChannelLogger): string {
78 if (!container.data) {
79 return "";
80 }
81
82 try {
83 const isGzip = getHeaderValue(container.headers, "Content-Encoding") === "gzip";
84 if (isGzip) {
85 try {
86 const binStr = Base64.atob(container.data);
87 const dataArr = new Uint8Array(binStr.length);
88 for (let i = 0; i < binStr.length; i++) {
89 dataArr[i] = binStr.charCodeAt(i);
90 }
91 // The request is gzipped, so convert the base64 back to the raw bytes first,
92 // then inflate. pako will detect the BOM headers and return a proper utf-8 string right away
93 return pako.inflate(dataArr, { to: "string" });
94 } catch (e) {
95 // on iOS, the stream send to flipper is already inflated, so the content-encoding will not
96 // match the actual data anymore, and we should skip inflating.
97 // In that case, we intentionally fall-through
98 if (!e.toString().includes("incorrect header check")) {
99 throw e;
100 }
101 }
102 }
103 // If this is not a gzipped request, assume we are interested in a proper utf-8 string.
104 // - If the raw binary data in is needed, in base64 form, use container.data directly
105 // - either directly use container.data (for example)
106 return Base64.decode(container.data);
107 } catch (err) {
108 logger?.debug(
109 `Network inspector failed to decode request/response body (size: ${
110 container.data.length
111 }): ${String(err.toString())}`,
112 );
113 return "";
114 }
115}
116
117/**
118 * @preserve
119 * End region: https://github.com/facebook/flipper/blob/v0.79.1/desktop/plugins/network/utils.tsx#L23-L60
120 */
121
122/**
123 * @preserve
124 * Start region: the code is borrowed from https://github.com/facebook/flipper/blob/v0.79.1/desktop/plugins/network/utils.tsx#L14-L21
125 *
126 * Copyright (c) Facebook, Inc. and its affiliates.
127 *
128 * This source code is licensed under the MIT license found in the
129 * LICENSE file in the root directory of this source tree.
130 *
131 * @format
132 */
133export function getHeaderValue(headers: Array<Header>, key: string): string {
134 for (const header of headers) {
135 if (header.key.toLowerCase() === key.toLowerCase()) {
136 return header.value;
137 }
138 }
139 return "";
140}
141
142/**
143 * @preserve
144 * End region: https://github.com/facebook/flipper/blob/v0.79.1/desktop/plugins/network/utils.tsx#L14-L21
145 */
146