Networking/QHTTPOperation.h
/* |
File: QHTTPOperation.h |
Contains: An NSOperation that runs an HTTP request. |
Written by: DTS |
Copyright: Copyright (c) 2010 Apple Inc. All Rights Reserved. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. |
("Apple") in consideration of your agreement to the following |
terms, and your use, installation, modification or |
redistribution of this Apple software constitutes acceptance of |
these terms. If you do not agree with these terms, please do |
not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following |
terms, and subject to these terms, Apple grants you a personal, |
non-exclusive license, under Apple's copyrights in this |
original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or |
without modifications, in source and/or binary forms; provided |
that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the |
following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks |
or logos of Apple Inc. may be used to endorse or promote |
products derived from the Apple Software without specific prior |
written permission from Apple. Except as expressly stated in |
this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any |
patent rights that may be infringed by your derivative works or |
by other works in which the Apple Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. |
APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING |
WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, |
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING |
THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, |
INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY |
OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY |
OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR |
OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF |
SUCH DAMAGE. |
*/ |
#import "QRunLoopOperation.h" |
/* |
QHTTPOperation is a general purpose NSOperation that runs an HTTP request. |
You initialise it with an HTTP request and then, when you run the operation, |
it sends the request and gathers the response. It is quite a complex |
object because it handles a wide variety of edge cases, but it's very |
easy to use in simple cases: |
1. create the operation with the URL you want to get |
op = [[[QHTTPOperation alloc] initWithURL:url] autorelease]; |
2. set up any non-default parameters, for example, set which HTTP |
content types are acceptable |
op.acceptableContentTypes = [NSSet setWithObject:@"text/html"]; |
3. enqueue the operation |
[queue addOperation:op]; |
4. finally, when the operation is done, use the lastResponse and |
error properties to find out how things went |
As mentioned above, QHTTPOperation is very general purpose. There are a |
large number of configuration and result options available to you. |
o You can specify a NSURLRequest rather than just a URL. |
o You can configure the run loop and modes on which the NSURLConnection is |
scheduled. |
o You can specify what HTTP status codes and content types are OK. |
o You can set an authentication delegate to handle authentication challenges. |
o You can accumulate responses in memory or in an NSOutputStream. |
o For in-memory responses, you can specify a default response size |
(used to size the response buffer) and a maximum response size |
(to prevent unbounded memory use). |
o You can get at the last request and the last response, to track |
redirects. |
o There are a variety of funky debugging options to simulator errors |
and delays. |
Finally, it's perfectly reasonable to subclass QHTTPOperation to meet you |
own specific needs. Specifically, it's common for the subclass to |
override -connection:didReceiveResponse: in order to setup the output |
stream based on the specific details of the response. |
*/ |
@protocol QHTTPOperationAuthenticationDelegate; |
@interface QHTTPOperation : QRunLoopOperation /* <NSURLConnectionDelegate> */ |
{ |
NSURLRequest * _request; |
NSIndexSet * _acceptableStatusCodes; |
NSSet * _acceptableContentTypes; |
id<QHTTPOperationAuthenticationDelegate> _authenticationDelegate; |
NSOutputStream * _responseOutputStream; |
NSUInteger _defaultResponseSize; |
NSUInteger _maximumResponseSize; |
NSURLConnection * _connection; |
BOOL _firstData; |
NSMutableData * _dataAccumulator; |
NSURLRequest * _lastRequest; |
NSHTTPURLResponse * _lastResponse; |
NSData * _responseBody; |
#if ! defined(NDEBUG) |
NSError * _debugError; |
NSTimeInterval _debugDelay; |
NSTimer * _debugDelayTimer; |
#endif |
} |
- (id)initWithRequest:(NSURLRequest *)request; // designated |
- (id)initWithURL:(NSURL *)url; // convenience, calls +[NSURLRequest requestWithURL:] |
// Things that are configured by the init method and can't be changed. |
@property (copy, readonly) NSURLRequest * request; |
@property (copy, readonly) NSURL * URL; |
// Things you can configure before queuing the operation. |
// runLoopThread and runLoopModes inherited from QRunLoopOperation |
@property (copy, readwrite) NSIndexSet * acceptableStatusCodes; // default is nil, implying 200..299 |
@property (copy, readwrite) NSSet * acceptableContentTypes; // default is nil, implying anything is acceptable |
@property (assign, readwrite) id<QHTTPOperationAuthenticationDelegate> authenticationDelegate; |
#if ! defined(NDEBUG) |
@property (copy, readwrite) NSError * debugError; // default is nil |
@property (assign, readwrite) NSTimeInterval debugDelay; // default is none |
#endif |
// Things you can configure up to the point where you start receiving data. |
// Typically you would change these in -connection:didReceiveResponse:, but |
// it is possible to change them up to the point where -connection:didReceiveData: |
// is called for the first time (that is, you could override -connection:didReceiveData: |
// and change these before calling super). |
// IMPORTANT: If you set a response stream, QHTTPOperation calls the response |
// stream synchronously. This is fine for file and memory streams, but it would |
// not work well for other types of streams (like a bound pair). |
@property (retain, readwrite) NSOutputStream * responseOutputStream; // defaults to nil, which puts response into responseBody |
@property (assign, readwrite) NSUInteger defaultResponseSize; // default is 1 MB, ignored if responseOutputStream is set |
@property (assign, readwrite) NSUInteger maximumResponseSize; // default is 4 MB, ignored if responseOutputStream is set |
// defaults are 1/4 of the above on embedded |
// Things that are only meaningful after a response has been received; |
@property (assign, readonly, getter=isStatusCodeAcceptable) BOOL statusCodeAcceptable; |
@property (assign, readonly, getter=isContentTypeAcceptable) BOOL contentTypeAcceptable; |
// Things that are only meaningful after the operation is finished. |
// error property inherited from QRunLoopOperation |
@property (copy, readonly) NSURLRequest * lastRequest; |
@property (copy, readonly) NSHTTPURLResponse * lastResponse; |
@property (copy, readonly) NSData * responseBody; |
@end |
@interface QHTTPOperation (NSURLConnectionDelegate) |
// QHTTPOperation implements all of these methods, so if you override them |
// you must consider whether or not to call super. |
// |
// These will be called on the operation's run loop thread. |
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace; |
// Routes the request to the authentication delegate if it exists, otherwise |
// just returns NO. |
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; |
// Routes the request to the authentication delegate if it exists, otherwise |
// just cancels the challenge. |
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response; |
// Latches the request and response in lastRequest and lastResponse. |
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response; |
// Latches the response in lastResponse. |
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data; |
// If this is the first chunk of data, it decides whether the data is going to be |
// routed to memory (responseBody) or a stream (responseOutputStream) and makes the |
// appropriate preparations. For this and subsequent data it then actually shuffles |
// the data to its destination. |
- (void)connectionDidFinishLoading:(NSURLConnection *)connection; |
// Completes the operation with either no error (if the response status code is acceptable) |
// or an error (otherwise). |
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error; |
// Completes the operation with the error. |
@end |
@protocol QHTTPOperationAuthenticationDelegate <NSObject> |
@required |
// These are called on the operation's run loop thread and have the same semantics as their |
// NSURLConnection equivalents. It's important to realise that there is no |
// didCancelAuthenticationChallenge callback (because NSURLConnection doesn't issue one to us). |
// Rather, an authentication delegate is expected to observe the operation and cancel itself |
// if the operation completes while the challenge is running. |
- (BOOL)httpOperation:(QHTTPOperation *)operation canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace; |
- (void)httpOperation:(QHTTPOperation *)operation didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge; |
@end |
extern NSString * kQHTTPOperationErrorDomain; |
// positive error codes are HTML status codes (when they are not allowed via acceptableStatusCodes) |
// |
// 0 is, of course, not a valid error code |
// |
// negative error codes are errors from the module |
enum { |
kQHTTPOperationErrorResponseTooLarge = -1, |
kQHTTPOperationErrorOnOutputStream = -2, |
kQHTTPOperationErrorBadContentType = -3 |
}; |
Copyright © 2010 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2010-10-22