Skip to content

Instantly share code, notes, and snippets.

@yashigani
Created July 13, 2013 01:57
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yashigani/5989077 to your computer and use it in GitHub Desktop.
Save yashigani/5989077 to your computer and use it in GitHub Desktop.
A simple CFNetwork wrapper
#import <Foundation/Foundation.h>
@interface YSNetwork : NSObject
@property (readonly) NSURL *URL;
@property (strong, nonatomic) NSURLRequest *request;
@property (strong, nonatomic) NSString *userAgent;
@property (strong, nonatomic, readonly) NSData *responseData;
@property (strong, nonatomic, readonly) NSDictionary *responseHeader;
@property (strong, nonatomic) void (^completion)(NSDictionary *responseHeader, NSData *responseData);
@property (strong, nonatomic) void (^failure)(NSError *error);
- (void)start;
- (void)startSynchronous;
@end
#import "YSNetwork.h"
@interface YSNetwork ()
@property (strong, nonatomic) NSMutableData *buffer;
@property (strong, nonatomic) NSData *responseData;
@property (strong, nonatomic) NSDictionary *responseHeader;
@end
@implementation YSNetwork
- (NSURL *)URL
{
return self.request.URL;
}
- (void)start
{
CFReadStreamRef stream = [self prepare];
if (stream) {
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(q, ^{
[self pollingStream:stream];
});
}
else {
[self raiseError:stream];
}
}
- (void)startSynchronous
{
CFReadStreamRef stream = [self prepare];
[self pollingStream:stream];
}
- (CFReadStreamRef)prepare
{
self.buffer = NSMutableData.data;
CFHTTPMessageRef message =
CFHTTPMessageCreateRequest(kCFAllocatorDefault, (CFStringRef)@"GET",
(__bridge CFURLRef)(self.URL), kCFHTTPVersion1_1);
for (NSString *field in self.request.allHTTPHeaderFields) {
NSString *value = self.request.allHTTPHeaderFields[field];
CFHTTPMessageSetHeaderFieldValue(message,
(__bridge CFStringRef)field,
(__bridge CFStringRef)value);
}
CFReadStreamRef stream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, message);
BOOL open = CFReadStreamOpen(stream);
return open ? stream : NULL;
}
- (void)pollingStream:(CFReadStreamRef)stream
{
BOOL done = NO;
NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:300];
NSDictionary *header = nil;
NSUInteger contentLength = 0;
while (!done) {
if (!CFReadStreamHasBytesAvailable(stream)) {
if ([NSDate.date timeIntervalSinceDate:timeout] > 0) {
[self raiseError:stream];
break;
}
continue;
}
if (!header) {
CFHTTPMessageRef response =
(CFHTTPMessageRef)CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader);
header = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(response));
contentLength = (NSUInteger)[header[@"Content-Length"] integerValue];
CFRelease(response);
self.responseHeader = header;
}
UInt8 buf[1024];
CFIndex read = CFReadStreamRead(stream, buf, 1024);
if (read < 0) {
[self raiseError:stream];
}
else {
[self.buffer appendBytes:buf length:read];
if (self.buffer.length == contentLength) {
self.responseData = self.buffer;
if (self.completion) {
self.completion(header, self.buffer);
}
done = YES;
}
}
}
CFReadStreamClose(stream);
CFRelease(stream);
}
- (void)raiseError:(CFReadStreamRef)stream
{
CFErrorRef e = CFReadStreamCopyError(stream);
if (self.failure) {
self.failure((__bridge NSError *)(e));
}
CFRelease(e);
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment