FLEXNetworkTransactionDetailTableViewController.m 21 KB

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