Skip to content

Commit 81ad713

Browse files
committed
Added Gzip support
Summary: Added Gzip function to RCTUtils. This uses dlopen to load the zlib library at runtime so there's no need to link it into your project. The main reason for this feature is to support gzipping of HTTP request bodies. Now, if you add 'Content-Encoding:gzip' to your request headers when using XMLHttpRequest, your request body will be automatically gzipped on the native side before sending. (Note: Gzip decoding of *response* bodies is handled automatically by iOS, and was already available).
1 parent 36f76e5 commit 81ad713

File tree

3 files changed

+65
-4
lines changed

3 files changed

+65
-4
lines changed

Libraries/Network/RCTNetworking.m

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,15 @@ - (void)buildRequest:(NSDictionary *)query
249249
request.HTTPBody = result[@"body"];
250250
NSString *contentType = result[@"contentType"];
251251
if (contentType) {
252-
[request setValue:contentType forHTTPHeaderField:@"content-type"];
252+
[request setValue:contentType forHTTPHeaderField:@"Content-Type"];
253253
}
254+
255+
// Gzip the request body
256+
if ([request.allHTTPHeaderFields[@"Content-Encoding"] isEqualToString:@"gzip"]) {
257+
request.HTTPBody = RCTGzipData(request.HTTPBody, -1 /* default */);
258+
[request setValue:[@(request.HTTPBody.length) description] forHTTPHeaderField:@"Content-Length"];
259+
}
260+
254261
[self sendRequest:request
255262
incrementalUpdates:incrementalUpdates
256263
responseSender:responseSender];

React/Base/RCTUtils.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#import "RCTAssert.h"
1616
#import "RCTDefines.h"
1717

18-
// Utility functions for JSON object <-> string serialization/deserialization
18+
// JSON serialization/deserialization
1919
RCT_EXTERN NSString *RCTJSONStringify(id jsonObject, NSError **error);
2020
RCT_EXTERN id RCTJSONParse(NSString *jsonString, NSError **error);
2121
RCT_EXTERN id RCTJSONParseMutable(NSString *jsonString, NSError **error);
@@ -24,7 +24,7 @@ RCT_EXTERN id RCTJSONParseWithOptions(NSString *jsonString, NSError **error, NSJ
2424
// Strip non JSON-safe values from an object graph
2525
RCT_EXTERN id RCTJSONClean(id object);
2626

27-
// Get MD5 hash of a string (TODO: currently unused. Remove?)
27+
// Get MD5 hash of a string
2828
RCT_EXTERN NSString *RCTMD5Hash(NSString *string);
2929

3030
// Get screen metrics in a thread-safe way
@@ -64,3 +64,6 @@ RCT_EXTERN id RCTNullIfNil(id value);
6464

6565
// Convert data to a Base64-encoded data URL
6666
RCT_EXTERN NSURL *RCTDataURL(NSString *mimeType, NSData *data);
67+
68+
// Gzip functionality - compression level in range 0 - 1 (-1 for default)
69+
RCT_EXTERN NSData *RCTGzipData(NSData *data, float level);

React/Base/RCTUtils.m

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@
1010
#import "RCTUtils.h"
1111

1212
#import <mach/mach_time.h>
13-
#import <objc/runtime.h>
13+
#import <objc/message.h>
1414

1515
#import <UIKit/UIKit.h>
1616

1717
#import <CommonCrypto/CommonCrypto.h>
1818

19+
#import <zlib.h>
20+
#import <dlfcn.h>
21+
1922
#import "RCTLog.h"
2023

2124
NSString *RCTJSONStringify(id jsonObject, NSError **error)
@@ -305,3 +308,51 @@ id RCTNilIfNull(id value)
305308
[data base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0]]];
306309
}
307310

311+
static BOOL RCTIsGzippedData(NSData *data)
312+
{
313+
UInt8 *bytes = (UInt8 *)data.bytes;
314+
return (data.length >= 2 && bytes[0] == 0x1f && bytes[1] == 0x8b);
315+
}
316+
317+
NSData *RCTGzipData(NSData *input, float level)
318+
{
319+
if (input.length == 0 || RCTIsGzippedData(input)) {
320+
return input;
321+
}
322+
323+
void *libz = dlopen("libz.dylib", RTLD_NOW);
324+
int (*deflateInit2_)(z_streamp, int, int, int, int, int, const char *, int) = dlsym(libz, "deflateInit2_");
325+
int (*deflate)(z_streamp, int) = dlsym(libz, "deflate");
326+
int (*deflateEnd)(z_streamp) = dlsym(libz, "deflateEnd");
327+
328+
z_stream stream;
329+
stream.zalloc = Z_NULL;
330+
stream.zfree = Z_NULL;
331+
stream.opaque = Z_NULL;
332+
stream.avail_in = (uint)input.length;
333+
stream.next_in = (Bytef *)input.bytes;
334+
stream.total_out = 0;
335+
stream.avail_out = 0;
336+
337+
static const NSUInteger RCTGZipChunkSize = 16384;
338+
339+
NSMutableData *output = nil;
340+
int compression = (level < 0.0f)? Z_DEFAULT_COMPRESSION: (int)(roundf(level * 9));
341+
if (deflateInit2(&stream, compression, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) == Z_OK) {
342+
output = [NSMutableData dataWithLength:RCTGZipChunkSize];
343+
while (stream.avail_out == 0) {
344+
if (stream.total_out >= output.length) {
345+
output.length += RCTGZipChunkSize;
346+
}
347+
stream.next_out = (uint8_t *)output.mutableBytes + stream.total_out;
348+
stream.avail_out = (uInt)(output.length - stream.total_out);
349+
deflate(&stream, Z_FINISH);
350+
}
351+
deflateEnd(&stream);
352+
output.length = stream.total_out;
353+
}
354+
355+
dlclose(libz);
356+
357+
return output;
358+
}

0 commit comments

Comments
 (0)