//
// P4NetworkOperation.m
// Perforce
//
// Created by Adam Czubernat on 08/01/2014.
// Copyright (c) 2014 Perforce Software, Inc. All rights reserved.
//
#import "P4NetworkOperation.h"
@interface P4NetworkOperation () <NSURLConnectionDelegate> {
// Internal state
BOOL started, executing, finished, cancelled;
void (^receive)(P4NetworkOperation *operation);
void (^completion)(P4NetworkOperation *operation);
NSString *MIMEType;
NSDictionary *HTTPHeaders;
NSInteger HTTPStatus;
}
- (void)cancelOperation;
- (void)completeOperation;
@end
@implementation P4NetworkOperation
@synthesize success, error;
#pragma mark - Overrides
- (void)start {
started = YES;
if ([self isCancelled]) {
[self cancelOperation];
return;
}
[self willChangeValueForKey:@"isExecuting"];
executing = YES;
[self didChangeValueForKey:@"isExecuting"];
// Create connection
connection = [[NSURLConnection alloc]
initWithRequest:request
delegate:self
startImmediately:NO];
data = [[NSMutableData alloc] init];
// Force runloop for events passing
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[connection start];
}
- (void)cancel {
[super cancel];
[self cancelOperation];
}
- (BOOL)isConcurrent {
return YES;
}
- (BOOL)isExecuting {
return executing;
}
- (BOOL)isFinished {
return finished;
}
- (void)dealloc {
PSLogm(@"");
}
#pragma mark - Public
+ (P4NetworkOperation *)operation {
return [[P4NetworkOperation alloc] init];
}
+ (P4NetworkOperation *)operationWithRequest:(NSURLRequest *)urlRequest receive:(P4NetworkBlock_t)receive completion:(P4NetworkBlock_t)completion {
P4NetworkOperation *operation = [[P4NetworkOperation alloc] init];
[operation setRequest:urlRequest receive:receive completion:completion];
return operation;
}
+ (P4NetworkOperation *)operationWithUrl:(NSString *)url HTTPMethod:(NSString *)method HTTPHeaders:(NSDictionary *)headers HTTPBody:(NSData *)body receive:(P4NetworkBlock_t)receive completion:(P4NetworkBlock_t)completion {
NSMutableURLRequest *request;
request = [NSMutableURLRequest
requestWithURL:[NSURL URLWithString:url]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60.0f];
[request setHTTPShouldHandleCookies:YES];
[request setHTTPMethod:method];
[request setHTTPBody:body];
// Add headers to request
for (NSString *field in headers)
[request setValue:[headers objectForKey:field] forHTTPHeaderField:field];
// Add length header
[request
setValue:[NSString stringWithFormat:@"%ld", [body length]]
forHTTPHeaderField:@"Content-Length"];
P4NetworkOperation *operation = [[P4NetworkOperation alloc] init];
[operation setRequest:request receive:receive completion:completion];
return operation;
}
- (NSData *)data { return data; }
- (NSString *)MIMEType { return MIMEType; }
- (NSDictionary *)HTTPHeaders { return HTTPHeaders; }
- (NSInteger)HTTPStatus { return HTTPStatus; }
- (NSDate *)HTTPDate {
static NSDateFormatter *formatter;
if (!formatter) {
formatter = [[NSDateFormatter alloc] init];
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
[formatter setLocale:locale];
[formatter setDateFormat:@"EEE, d MMM y HH:mm:ss ZZZ"];
}
NSString *stringDate = [HTTPHeaders objectForKey:@"Date"];
if (!stringDate)
return nil;
return [formatter dateFromString:stringDate];
}
- (NSURLResponse *)HTTPResponse { return HTTPResponse; }
- (CGFloat)progress { return progress; }
- (long)completed { return completed; }
- (long)length { return length; }
- (NSURLRequest *)request {
return request;
}
- (void)setRequest:(NSURLRequest *)urlRequest receive:(P4NetworkBlock_t)onReceive completion:(P4NetworkBlock_t)onCompletion {
// Don't create new request if connection is already running
if (executing)
return;
// Copy passed request
request = [urlRequest copy];
// Copy passed blocks
receive = [onReceive copy]; // Copy blocks from stack to heap, stack
completion = [onCompletion copy]; // otherwise would be drained when calling
// function returns.
// Clear variables
connection = nil;
success = NO;
error = nil;
data = nil;
MIMEType = nil;
HTTPHeaders = nil;
HTTPResponse = nil;
HTTPStatus = 0;
progress = 0.0f;
completed = 0;
length = 0;
}
#pragma mark - Private
- (void)cancelOperation {
@synchronized(self) {
if (cancelled)
return;
cancelled = YES;
}
[connection cancel];
connection = nil;
// Imitate error situation
[self connection:nil didFailWithError:
[NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]];
}
- (void)completeOperation {
if (started)
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
executing = NO;
finished = YES;
[self didChangeValueForKey:@"isExecuting"];
if (started)
[self didChangeValueForKey:@"isFinished"];
if (completion)
completion(self);
}
#pragma mark - NSURLConnection delegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
length = [response expectedContentLength];
completed = 0;
progress = 0.0f;
HTTPResponse = (NSHTTPURLResponse *)response;
MIMEType = [HTTPResponse MIMEType];
HTTPHeaders = [HTTPResponse allHeaderFields];
HTTPStatus = [HTTPResponse statusCode];
if (receive)
receive(self);
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)receivedData {
// if (outputFile)
// data = receivedData; // Store new chunk of data
// else
[(NSMutableData *)data appendData:receivedData]; // Append new data
// Add received bytecount into completedLength
completed += receivedData.length;
// Compute progress
if (length == NSURLResponseUnknownLength)
progress = 0.0;
else
progress = completed / (CGFloat) length;
if (receive)
receive(self);
}
- (void)connection:(NSURLConnection *)failedConnection didFailWithError:(NSError *)failError {
connection = nil;
success = NO;
if (failError)
error = failError;
[self completeOperation];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)finishedConnection {
connection = nil;
success = YES;
if ((HTTPStatus >= 400 && HTTPStatus < 500) || // Client error
(HTTPStatus >= 500 && HTTPStatus < 600)) { // Server error
if (!error)
error = [NSError errorWithDomain:NSCocoaErrorDomain code:HTTPStatus userInfo:@{
NSLocalizedDescriptionKey : [NSHTTPURLResponse localizedStringForStatusCode:HTTPStatus]
}];
success = NO;
}
[self completeOperation];
}
@end
# |
Change |
User |
Description |
Committed |
|
#1
|
10691 |
DWishR |
Populate //guest/DWishR/piper/.... |
|
|
//guest/perforce_software/piper/Perforce/Classes/Client/P4NetworkOperation.m |
#1
|
8919 |
Matt Attaway |
Initial add of Piper, a lightweight Perforce client for artists and designers. |
|
|