Skip to content

Commit c8e09e9

Browse files
huntiemeta-codesync[bot]
authored andcommitted
Add support for gzipped request body previews (#54963)
Summary: Pull Request resolved: #54963 **Context** Follows D89659683, addressing a coverage gap in processing network request body previews when the payload is gzipped. **This diff** - Updates `convertRequestBodyToStringTruncated` to attempt gzip decompression based on `"Content-Encoding"` header. - Adds new `RCTDecompressGzipData` util in `RCTUtils`. - Also reduce the maximum size of request body previews from 1MB to 512KB. Changelog: [Internal] Changelog (0.83.x): [iOS][Fixed] - React Native DevTools: Add support for gzipped request body previews. Reviewed By: cipolleschi Differential Revision: D89659684 fbshipit-source-id: 513143cc683d003ae6f1fde69f75e9ca51181771
1 parent c00173f commit c8e09e9

3 files changed

Lines changed: 74 additions & 3 deletions

File tree

packages/react-native/Libraries/Network/RCTInspectorNetworkReporter.mm

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#import "RCTNetworkConversions.h"
1111

1212
#import <React/RCTLog.h>
13+
#import <React/RCTUtils.h>
1314
#import <react/networking/NetworkReporter.h>
1415

1516
using namespace facebook::react;
@@ -28,13 +29,22 @@ Headers convertNSDictionaryToHeaders(const NSDictionary<NSString *, NSString *>
2829

2930
std::string convertRequestBodyToStringTruncated(NSURLRequest *request)
3031
{
31-
const NSUInteger maxBodySize = 1024 * 1024; // 1MB
32+
const NSUInteger maxBodySize = 512 * 1024; // 512KB
3233
NSData *bodyData = request.HTTPBody;
3334

3435
if (bodyData == nil || bodyData.length == 0) {
3536
return "";
3637
}
3738

39+
// Decompress if gzip-encoded (up to maxBodySize)
40+
NSString *contentEncoding = [request valueForHTTPHeaderField:@"Content-Encoding"];
41+
if ([contentEncoding isEqualToString:@"gzip"]) {
42+
NSData *decompressedData = RCTDecompressGzipData(bodyData, maxBodySize);
43+
if (decompressedData != nil) {
44+
bodyData = decompressedData;
45+
}
46+
}
47+
3848
auto bodyLength = bodyData.length;
3949
auto bytesToRead = std::min(bodyLength, maxBodySize);
4050
NSData *truncatedData = [bodyData subdataWithRange:NSMakeRange(0, bytesToRead)];

packages/react-native/React/Base/RCTUtils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ RCT_EXTERN NSURL *RCTDataURL(NSString *mimeType, NSData *data);
132132
// Gzip functionality - compression level in range 0 - 1 (-1 for default)
133133
RCT_EXTERN NSData *__nullable RCTGzipData(NSData *__nullable data, float level);
134134

135+
// Gzip decompression - maxDecompressedSize of 0 means no limit, returns nil if
136+
// limit exceeded or decompression fails
137+
RCT_EXTERN NSData *__nullable RCTDecompressGzipData(NSData *__nullable data, NSUInteger maxDecompressedSize);
138+
135139
// Returns the relative path within the main bundle for an absolute URL
136140
// (or nil, if the URL does not specify a path within the main bundle)
137141
RCT_EXTERN NSString *__nullable RCTBundlePathForURL(NSURL *__nullable URL);

packages/react-native/React/Base/RCTUtils.mm

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,8 @@ BOOL RCTIsGzippedData(NSData *__nullable data)
713713
return (data.length >= 2 && bytes[0] == 0x1f && bytes[1] == 0x8b);
714714
}
715715

716+
static const NSUInteger RCTGZipChunkSize = 16384;
717+
716718
NSData *__nullable RCTGzipData(NSData *__nullable input, float level)
717719
{
718720
if (input.length == 0 || RCTIsGzippedData(input)) {
@@ -739,8 +741,6 @@ BOOL RCTIsGzippedData(NSData *__nullable data)
739741
stream.total_out = 0;
740742
stream.avail_out = 0;
741743

742-
static const NSUInteger RCTGZipChunkSize = 16384;
743-
744744
NSMutableData *output = nil;
745745
int compression = (level < 0.0f) ? Z_DEFAULT_COMPRESSION : (int)(roundf(level * 9));
746746
if (deflateInit2(&stream, compression, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) == Z_OK) {
@@ -762,6 +762,63 @@ BOOL RCTIsGzippedData(NSData *__nullable data)
762762
return output;
763763
}
764764

765+
NSData *__nullable RCTDecompressGzipData(NSData *__nullable data, NSUInteger maxDecompressedSize)
766+
{
767+
if (data.length == 0 || !RCTIsGzippedData(data)) {
768+
return data;
769+
}
770+
771+
void *libz = dlopen("/usr/lib/libz.dylib", RTLD_LAZY);
772+
773+
using InflateInit2_ = int (*)(z_streamp, int, const char *, int);
774+
InflateInit2_ inflateInit2_ = (InflateInit2_)dlsym(libz, "inflateInit2_");
775+
776+
using Inflate = int (*)(z_streamp, int);
777+
Inflate inflate = (Inflate)dlsym(libz, "inflate");
778+
779+
using InflateEnd = int (*)(z_streamp);
780+
InflateEnd inflateEnd = (InflateEnd)dlsym(libz, "inflateEnd");
781+
782+
z_stream stream;
783+
stream.zalloc = Z_NULL;
784+
stream.zfree = Z_NULL;
785+
stream.opaque = Z_NULL;
786+
stream.avail_in = (uint)data.length;
787+
stream.next_in = (Bytef *)data.bytes;
788+
stream.total_out = 0;
789+
stream.avail_out = 0;
790+
791+
NSMutableData *output = nil;
792+
// Use 31 for windowBits to enable gzip decoding (15 + 16)
793+
if (inflateInit2(&stream, 31) == Z_OK) {
794+
output = [NSMutableData dataWithLength:RCTGZipChunkSize];
795+
int status = Z_OK;
796+
while (status == Z_OK) {
797+
if (stream.total_out >= output.length) {
798+
output.length += RCTGZipChunkSize;
799+
}
800+
if (maxDecompressedSize > 0 && stream.total_out >= maxDecompressedSize) {
801+
inflateEnd(&stream);
802+
dlclose(libz);
803+
return nil;
804+
}
805+
stream.next_out = (uint8_t *)output.mutableBytes + stream.total_out;
806+
stream.avail_out = (uInt)(output.length - stream.total_out);
807+
status = inflate(&stream, Z_SYNC_FLUSH);
808+
}
809+
inflateEnd(&stream);
810+
if (status != Z_STREAM_END) {
811+
dlclose(libz);
812+
return nil;
813+
}
814+
output.length = stream.total_out;
815+
}
816+
817+
dlclose(libz);
818+
819+
return output;
820+
}
821+
765822
static NSString *RCTRelativePathForURL(NSString *basePath, NSURL *__nullable URL)
766823
{
767824
if (!URL.fileURL) {

0 commit comments

Comments
 (0)