FLEXNetworkTransactionDetailTableViewController.m 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. //
  2. // FLEXNetworkTransactionDetailTableViewController.m
  3. // Flipboard
  4. //
  5. // Created by Ryan Olson on 2/10/15.
  6. // Copyright (c) 2015 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXNetworkTransactionDetailTableViewController.h"
  9. #import "FLEXNetworkCurlLogger.h"
  10. #import "FLEXNetworkRecorder.h"
  11. #import "FLEXNetworkTransaction.h"
  12. #import "FLEXWebViewController.h"
  13. #import "FLEXImagePreviewViewController.h"
  14. #import "FLEXMultilineTableViewCell.h"
  15. #import "FLEXUtility.h"
  16. #import "FLEXManager+Private.h"
  17. typedef UIViewController *(^FLEXNetworkDetailRowSelectionFuture)(void);
  18. @interface FLEXNetworkDetailRow : NSObject
  19. @property (nonatomic, copy) NSString *title;
  20. @property (nonatomic, copy) NSString *detailText;
  21. @property (nonatomic, copy) FLEXNetworkDetailRowSelectionFuture selectionFuture;
  22. @end
  23. @implementation FLEXNetworkDetailRow
  24. @end
  25. @interface FLEXNetworkDetailSection : NSObject
  26. @property (nonatomic, copy) NSString *title;
  27. @property (nonatomic, copy) NSArray<FLEXNetworkDetailRow *> *rows;
  28. @end
  29. @implementation FLEXNetworkDetailSection
  30. @end
  31. @interface FLEXNetworkTransactionDetailTableViewController ()
  32. @property (nonatomic, copy) NSArray<FLEXNetworkDetailSection *> *sections;
  33. @end
  34. @implementation FLEXNetworkTransactionDetailTableViewController
  35. - (instancetype)initWithStyle:(UITableViewStyle)style
  36. {
  37. // Force grouped style
  38. self = [super initWithStyle:UITableViewStyleGrouped];
  39. if (self) {
  40. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleTransactionUpdatedNotification:) name:kFLEXNetworkRecorderTransactionUpdatedNotification object:nil];
  41. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Copy curl" style:UIBarButtonItemStylePlain target:self action:@selector(copyButtonPressed:)];
  42. }
  43. return self;
  44. }
  45. - (void)viewDidLoad
  46. {
  47. [super viewDidLoad];
  48. [self.tableView registerClass:[FLEXMultilineTableViewCell class] forCellReuseIdentifier:kFLEXMultilineTableViewCellIdentifier];
  49. }
  50. - (void)setTransaction:(FLEXNetworkTransaction *)transaction
  51. {
  52. if (![_transaction isEqual:transaction]) {
  53. _transaction = transaction;
  54. self.title = [transaction.request.URL lastPathComponent];
  55. [self rebuildTableSections];
  56. }
  57. }
  58. - (void)setSections:(NSArray<FLEXNetworkDetailSection *> *)sections
  59. {
  60. if (![_sections isEqual:sections]) {
  61. _sections = [sections copy];
  62. [self.tableView reloadData];
  63. }
  64. }
  65. - (void)rebuildTableSections
  66. {
  67. NSMutableArray<FLEXNetworkDetailSection *> *sections = [NSMutableArray array];
  68. FLEXNetworkDetailSection *generalSection = [[self class] generalSectionForTransaction:self.transaction];
  69. if ([generalSection.rows count] > 0) {
  70. [sections addObject:generalSection];
  71. }
  72. FLEXNetworkDetailSection *requestHeadersSection = [[self class] requestHeadersSectionForTransaction:self.transaction];
  73. if ([requestHeadersSection.rows count] > 0) {
  74. [sections addObject:requestHeadersSection];
  75. }
  76. FLEXNetworkDetailSection *queryParametersSection = [[self class] queryParametersSectionForTransaction:self.transaction];
  77. if ([queryParametersSection.rows count] > 0) {
  78. [sections addObject:queryParametersSection];
  79. }
  80. FLEXNetworkDetailSection *postBodySection = [[self class] postBodySectionForTransaction:self.transaction];
  81. if ([postBodySection.rows count] > 0) {
  82. [sections addObject:postBodySection];
  83. }
  84. FLEXNetworkDetailSection *responseHeadersSection = [[self class] responseHeadersSectionForTransaction:self.transaction];
  85. if ([responseHeadersSection.rows count] > 0) {
  86. [sections addObject:responseHeadersSection];
  87. }
  88. self.sections = sections;
  89. }
  90. - (void)handleTransactionUpdatedNotification:(NSNotification *)notification
  91. {
  92. FLEXNetworkTransaction *transaction = [[notification userInfo] objectForKey:kFLEXNetworkRecorderUserInfoTransactionKey];
  93. if (transaction == self.transaction) {
  94. [self rebuildTableSections];
  95. }
  96. }
  97. - (void)copyButtonPressed:(id)sender
  98. {
  99. [[UIPasteboard generalPasteboard] setString:[FLEXNetworkCurlLogger curlCommandString:_transaction.request]];
  100. }
  101. #pragma mark - Table view data source
  102. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  103. {
  104. return [self.sections count];
  105. }
  106. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  107. {
  108. FLEXNetworkDetailSection *sectionModel = self.sections[section];
  109. return [sectionModel.rows count];
  110. }
  111. - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
  112. {
  113. FLEXNetworkDetailSection *sectionModel = self.sections[section];
  114. return sectionModel.title;
  115. }
  116. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  117. {
  118. FLEXMultilineTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXMultilineTableViewCellIdentifier forIndexPath:indexPath];
  119. FLEXNetworkDetailRow *rowModel = [self rowModelAtIndexPath:indexPath];
  120. cell.textLabel.attributedText = [[self class] attributedTextForRow:rowModel];
  121. cell.accessoryType = rowModel.selectionFuture ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
  122. cell.selectionStyle = rowModel.selectionFuture ? UITableViewCellSelectionStyleDefault : UITableViewCellSelectionStyleNone;
  123. return cell;
  124. }
  125. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  126. {
  127. FLEXNetworkDetailRow *rowModel = [self rowModelAtIndexPath:indexPath];
  128. UIViewController *viewControllerToPush = nil;
  129. if (rowModel.selectionFuture) {
  130. viewControllerToPush = rowModel.selectionFuture();
  131. }
  132. if (viewControllerToPush) {
  133. [self.navigationController pushViewController:viewControllerToPush animated:YES];
  134. }
  135. [tableView deselectRowAtIndexPath:indexPath animated:YES];
  136. }
  137. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
  138. {
  139. FLEXNetworkDetailRow *row = [self rowModelAtIndexPath:indexPath];
  140. NSAttributedString *attributedText = [[self class] attributedTextForRow:row];
  141. BOOL showsAccessory = row.selectionFuture != nil;
  142. return [FLEXMultilineTableViewCell preferredHeightWithAttributedText:attributedText inTableViewWidth:self.tableView.bounds.size.width style:UITableViewStyleGrouped showsAccessory:showsAccessory];
  143. }
  144. - (FLEXNetworkDetailRow *)rowModelAtIndexPath:(NSIndexPath *)indexPath
  145. {
  146. FLEXNetworkDetailSection *sectionModel = self.sections[indexPath.section];
  147. return sectionModel.rows[indexPath.row];
  148. }
  149. #pragma mark - Cell Copying
  150. - (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath
  151. {
  152. return YES;
  153. }
  154. - (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
  155. {
  156. return action == @selector(copy:);
  157. }
  158. - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
  159. {
  160. if (action == @selector(copy:)) {
  161. FLEXNetworkDetailRow *row = [self rowModelAtIndexPath:indexPath];
  162. [[UIPasteboard generalPasteboard] setString:row.detailText];
  163. }
  164. }
  165. #pragma mark - View Configuration
  166. + (NSAttributedString *)attributedTextForRow:(FLEXNetworkDetailRow *)row
  167. {
  168. NSDictionary<NSString *, id> *titleAttributes = @{ NSFontAttributeName : [UIFont fontWithName:@"HelveticaNeue-Medium" size:12.0],
  169. NSForegroundColorAttributeName : [UIColor colorWithWhite:0.5 alpha:1.0] };
  170. NSDictionary<NSString *, id> *detailAttributes = @{ NSFontAttributeName : [FLEXUtility defaultTableViewCellLabelFont],
  171. NSForegroundColorAttributeName : [UIColor blackColor] };
  172. NSString *title = [NSString stringWithFormat:@"%@: ", row.title];
  173. NSString *detailText = row.detailText ?: @"";
  174. NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] init];
  175. [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:title attributes:titleAttributes]];
  176. [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:detailText attributes:detailAttributes]];
  177. return attributedText;
  178. }
  179. #pragma mark - Table Data Generation
  180. + (FLEXNetworkDetailSection *)generalSectionForTransaction:(FLEXNetworkTransaction *)transaction
  181. {
  182. NSMutableArray<FLEXNetworkDetailRow *> *rows = [NSMutableArray array];
  183. FLEXNetworkDetailRow *requestURLRow = [[FLEXNetworkDetailRow alloc] init];
  184. requestURLRow.title = @"Request URL";
  185. NSURL *url = transaction.request.URL;
  186. requestURLRow.detailText = url.absoluteString;
  187. requestURLRow.selectionFuture = ^{
  188. UIViewController *urlWebViewController = [[FLEXWebViewController alloc] initWithURL:url];
  189. urlWebViewController.title = url.absoluteString;
  190. return urlWebViewController;
  191. };
  192. [rows addObject:requestURLRow];
  193. FLEXNetworkDetailRow *requestMethodRow = [[FLEXNetworkDetailRow alloc] init];
  194. requestMethodRow.title = @"Request Method";
  195. requestMethodRow.detailText = transaction.request.HTTPMethod;
  196. [rows addObject:requestMethodRow];
  197. if ([transaction.cachedRequestBody length] > 0) {
  198. FLEXNetworkDetailRow *postBodySizeRow = [[FLEXNetworkDetailRow alloc] init];
  199. postBodySizeRow.title = @"Request Body Size";
  200. postBodySizeRow.detailText = [NSByteCountFormatter stringFromByteCount:[transaction.cachedRequestBody length] countStyle:NSByteCountFormatterCountStyleBinary];
  201. [rows addObject:postBodySizeRow];
  202. FLEXNetworkDetailRow *postBodyRow = [[FLEXNetworkDetailRow alloc] init];
  203. postBodyRow.title = @"Request Body";
  204. postBodyRow.detailText = @"tap to view";
  205. postBodyRow.selectionFuture = ^{
  206. NSString *contentType = [transaction.request valueForHTTPHeaderField:@"Content-Type"];
  207. UIViewController *detailViewController = [self detailViewControllerForMIMEType:contentType data:[self postBodyDataForTransaction:transaction]];
  208. if (detailViewController) {
  209. detailViewController.title = @"Request Body";
  210. } else {
  211. NSString *alertMessage = [NSString stringWithFormat:@"FLEX does not have a viewer for request body data with MIME type: %@", [transaction.request valueForHTTPHeaderField:@"Content-Type"]];
  212. [[[UIAlertView alloc] initWithTitle:@"Can't View Body Data" message:alertMessage delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
  213. }
  214. return detailViewController;
  215. };
  216. [rows addObject:postBodyRow];
  217. }
  218. NSString *statusCodeString = [FLEXUtility statusCodeStringFromURLResponse:transaction.response];
  219. if ([statusCodeString length] > 0) {
  220. FLEXNetworkDetailRow *statusCodeRow = [[FLEXNetworkDetailRow alloc] init];
  221. statusCodeRow.title = @"Status Code";
  222. statusCodeRow.detailText = statusCodeString;
  223. [rows addObject:statusCodeRow];
  224. }
  225. if (transaction.error) {
  226. FLEXNetworkDetailRow *errorRow = [[FLEXNetworkDetailRow alloc] init];
  227. errorRow.title = @"Error";
  228. errorRow.detailText = transaction.error.localizedDescription;
  229. [rows addObject:errorRow];
  230. }
  231. FLEXNetworkDetailRow *responseBodyRow = [[FLEXNetworkDetailRow alloc] init];
  232. responseBodyRow.title = @"Response Body";
  233. NSData *responseData = [[FLEXNetworkRecorder defaultRecorder] cachedResponseBodyForTransaction:transaction];
  234. if ([responseData length] > 0) {
  235. responseBodyRow.detailText = @"tap to view";
  236. // Avoid a long lived strong reference to the response data in case we need to purge it from the cache.
  237. __weak NSData *weakResponseData = responseData;
  238. responseBodyRow.selectionFuture = ^{
  239. UIViewController *responseBodyDetailViewController = nil;
  240. NSData *strongResponseData = weakResponseData;
  241. if (strongResponseData) {
  242. responseBodyDetailViewController = [self detailViewControllerForMIMEType:transaction.response.MIMEType data:strongResponseData];
  243. if (!responseBodyDetailViewController) {
  244. NSString *alertMessage = [NSString stringWithFormat:@"FLEX does not have a viewer for responses with MIME type: %@", transaction.response.MIMEType];
  245. [[[UIAlertView alloc] initWithTitle:@"Can't View Response" message:alertMessage delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
  246. }
  247. responseBodyDetailViewController.title = @"Response";
  248. } else {
  249. NSString *alertMessage = @"The response has been purged from the cache";
  250. [[[UIAlertView alloc] initWithTitle:@"Can't View Response" message:alertMessage delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
  251. }
  252. return responseBodyDetailViewController;
  253. };
  254. } else {
  255. BOOL emptyResponse = transaction.receivedDataLength == 0;
  256. responseBodyRow.detailText = emptyResponse ? @"empty" : @"not in cache";
  257. }
  258. [rows addObject:responseBodyRow];
  259. FLEXNetworkDetailRow *responseSizeRow = [[FLEXNetworkDetailRow alloc] init];
  260. responseSizeRow.title = @"Response Size";
  261. responseSizeRow.detailText = [NSByteCountFormatter stringFromByteCount:transaction.receivedDataLength countStyle:NSByteCountFormatterCountStyleBinary];
  262. [rows addObject:responseSizeRow];
  263. FLEXNetworkDetailRow *mimeTypeRow = [[FLEXNetworkDetailRow alloc] init];
  264. mimeTypeRow.title = @"MIME Type";
  265. mimeTypeRow.detailText = transaction.response.MIMEType;
  266. [rows addObject:mimeTypeRow];
  267. FLEXNetworkDetailRow *mechanismRow = [[FLEXNetworkDetailRow alloc] init];
  268. mechanismRow.title = @"Mechanism";
  269. mechanismRow.detailText = transaction.requestMechanism;
  270. [rows addObject:mechanismRow];
  271. NSDateFormatter *startTimeFormatter = [[NSDateFormatter alloc] init];
  272. startTimeFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";
  273. FLEXNetworkDetailRow *localStartTimeRow = [[FLEXNetworkDetailRow alloc] init];
  274. localStartTimeRow.title = [NSString stringWithFormat:@"Start Time (%@)", [[NSTimeZone localTimeZone] abbreviationForDate:transaction.startTime]];
  275. localStartTimeRow.detailText = [startTimeFormatter stringFromDate:transaction.startTime];
  276. [rows addObject:localStartTimeRow];
  277. startTimeFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
  278. FLEXNetworkDetailRow *utcStartTimeRow = [[FLEXNetworkDetailRow alloc] init];
  279. utcStartTimeRow.title = @"Start Time (UTC)";
  280. utcStartTimeRow.detailText = [startTimeFormatter stringFromDate:transaction.startTime];
  281. [rows addObject:utcStartTimeRow];
  282. FLEXNetworkDetailRow *unixStartTime = [[FLEXNetworkDetailRow alloc] init];
  283. unixStartTime.title = @"Unix Start Time";
  284. unixStartTime.detailText = [NSString stringWithFormat:@"%f", [transaction.startTime timeIntervalSince1970]];
  285. [rows addObject:unixStartTime];
  286. FLEXNetworkDetailRow *durationRow = [[FLEXNetworkDetailRow alloc] init];
  287. durationRow.title = @"Total Duration";
  288. durationRow.detailText = [FLEXUtility stringFromRequestDuration:transaction.duration];
  289. [rows addObject:durationRow];
  290. FLEXNetworkDetailRow *latencyRow = [[FLEXNetworkDetailRow alloc] init];
  291. latencyRow.title = @"Latency";
  292. latencyRow.detailText = [FLEXUtility stringFromRequestDuration:transaction.latency];
  293. [rows addObject:latencyRow];
  294. FLEXNetworkDetailSection *generalSection = [[FLEXNetworkDetailSection alloc] init];
  295. generalSection.title = @"General";
  296. generalSection.rows = rows;
  297. return generalSection;
  298. }
  299. + (FLEXNetworkDetailSection *)requestHeadersSectionForTransaction:(FLEXNetworkTransaction *)transaction
  300. {
  301. FLEXNetworkDetailSection *requestHeadersSection = [[FLEXNetworkDetailSection alloc] init];
  302. requestHeadersSection.title = @"Request Headers";
  303. requestHeadersSection.rows = [self networkDetailRowsFromDictionary:transaction.request.allHTTPHeaderFields];
  304. return requestHeadersSection;
  305. }
  306. + (FLEXNetworkDetailSection *)postBodySectionForTransaction:(FLEXNetworkTransaction *)transaction
  307. {
  308. FLEXNetworkDetailSection *postBodySection = [[FLEXNetworkDetailSection alloc] init];
  309. postBodySection.title = @"Request Body Parameters";
  310. if ([transaction.cachedRequestBody length] > 0) {
  311. NSString *contentType = [transaction.request valueForHTTPHeaderField:@"Content-Type"];
  312. if ([contentType hasPrefix:@"application/x-www-form-urlencoded"]) {
  313. NSString *bodyString = [[NSString alloc] initWithData:[self postBodyDataForTransaction:transaction] encoding:NSUTF8StringEncoding];
  314. postBodySection.rows = [self networkDetailRowsFromDictionary:[FLEXUtility dictionaryFromQuery:bodyString]];
  315. }
  316. }
  317. return postBodySection;
  318. }
  319. + (FLEXNetworkDetailSection *)queryParametersSectionForTransaction:(FLEXNetworkTransaction *)transaction
  320. {
  321. NSDictionary<NSString *, id> *queryDictionary = [FLEXUtility dictionaryFromQuery:transaction.request.URL.query];
  322. FLEXNetworkDetailSection *querySection = [[FLEXNetworkDetailSection alloc] init];
  323. querySection.title = @"Query Parameters";
  324. querySection.rows = [self networkDetailRowsFromDictionary:queryDictionary];
  325. return querySection;
  326. }
  327. + (FLEXNetworkDetailSection *)responseHeadersSectionForTransaction:(FLEXNetworkTransaction *)transaction
  328. {
  329. FLEXNetworkDetailSection *responseHeadersSection = [[FLEXNetworkDetailSection alloc] init];
  330. responseHeadersSection.title = @"Response Headers";
  331. if ([transaction.response isKindOfClass:[NSHTTPURLResponse class]]) {
  332. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)transaction.response;
  333. responseHeadersSection.rows = [self networkDetailRowsFromDictionary:httpResponse.allHeaderFields];
  334. }
  335. return responseHeadersSection;
  336. }
  337. + (NSArray<FLEXNetworkDetailRow *> *)networkDetailRowsFromDictionary:(NSDictionary<NSString *, id> *)dictionary
  338. {
  339. NSMutableArray<FLEXNetworkDetailRow *> *rows = [NSMutableArray arrayWithCapacity:[dictionary count]];
  340. NSArray<NSString *> *sortedKeys = [[dictionary allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
  341. for (NSString *key in sortedKeys) {
  342. id value = dictionary[key];
  343. FLEXNetworkDetailRow *row = [[FLEXNetworkDetailRow alloc] init];
  344. row.title = key;
  345. row.detailText = [value description];
  346. [rows addObject:row];
  347. }
  348. return [rows copy];
  349. }
  350. + (UIViewController *)detailViewControllerForMIMEType:(NSString *)mimeType data:(NSData *)data
  351. {
  352. FLEXCustomContentViewerFuture makeCustomViewer = [FLEXManager sharedManager].customContentTypeViewers[mimeType.lowercaseString];
  353. if (makeCustomViewer) {
  354. UIViewController *viewer = makeCustomViewer(data);
  355. if (viewer) {
  356. return viewer;
  357. }
  358. }
  359. // FIXME (RKO): Don't rely on UTF8 string encoding
  360. UIViewController *detailViewController = nil;
  361. if ([FLEXUtility isValidJSONData:data]) {
  362. NSString *prettyJSON = [FLEXUtility prettyJSONStringFromData:data];
  363. if ([prettyJSON length] > 0) {
  364. detailViewController = [[FLEXWebViewController alloc] initWithText:prettyJSON];
  365. }
  366. } else if ([mimeType hasPrefix:@"image/"]) {
  367. UIImage *image = [UIImage imageWithData:data];
  368. detailViewController = [[FLEXImagePreviewViewController alloc] initWithImage:image];
  369. } else if ([mimeType isEqual:@"application/x-plist"]) {
  370. id propertyList = [NSPropertyListSerialization propertyListWithData:data options:0 format:NULL error:NULL];
  371. detailViewController = [[FLEXWebViewController alloc] initWithText:[propertyList description]];
  372. }
  373. // Fall back to trying to show the response as text
  374. if (!detailViewController) {
  375. NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  376. if ([text length] > 0) {
  377. detailViewController = [[FLEXWebViewController alloc] initWithText:text];
  378. }
  379. }
  380. return detailViewController;
  381. }
  382. + (NSData *)postBodyDataForTransaction:(FLEXNetworkTransaction *)transaction
  383. {
  384. NSData *bodyData = transaction.cachedRequestBody;
  385. if ([bodyData length] > 0) {
  386. NSString *contentEncoding = [transaction.request valueForHTTPHeaderField:@"Content-Encoding"];
  387. if ([contentEncoding rangeOfString:@"deflate" options:NSCaseInsensitiveSearch].length > 0 || [contentEncoding rangeOfString:@"gzip" options:NSCaseInsensitiveSearch].length > 0) {
  388. bodyData = [FLEXUtility inflatedDataFromCompressedData:bodyData];
  389. }
  390. }
  391. return bodyData;
  392. }
  393. @end