Index: src/OFHTTPRequest.h ================================================================== --- src/OFHTTPRequest.h +++ src/OFHTTPRequest.h @@ -17,19 +17,75 @@ #import "OFObject.h" @class OFString; @class OFDictionary; @class OFURL; +@class OFHTTPRequest; @class OFHTTPRequestResult; @class OFDataArray; typedef enum of_http_request_type_t { OF_HTTP_REQUEST_TYPE_GET, OF_HTTP_REQUEST_TYPE_POST, OF_HTTP_REQUEST_TYPE_HEAD } of_http_request_type_t; +/** + * \brief A delegate for OFHTTPRequests. + */ +#ifndef OF_HTTP_REQUEST_M +@protocol OFHTTPRequestDelegate +#else +@protocol OFHTTPRequestDelegate +#endif +#ifdef OF_HAVE_OPTIONAL_PROTOCOLS +@optional +#endif +/** + * This callback is called when the OFHTTPRequest received the headers. + * + * \param request The OFHTTPRequest which received the headers + * \param headers The headers received + * \param statusCode The status code received + */ +- (void)request: (OFHTTPRequest*)request + didReceiveHeaders: (OFDictionary*)headers + withStatusCode: (int)statusCode; + +/** + * This callback is called when the OFHTTPRequest received data. + * + * This is useful for example if you want to update a status display. + * + * \param request The OFHTTPRequest which received data + * \param data The data the OFHTTPRequest received + * \param len The length of the data received, in bytes + */ +- (void)request: (OFHTTPRequest*)request + didReceiveData: (const char*)data + withLength: (size_t)len; + +/** + * This callback is called when the OFHTTPRequest will follow a redirect. + * + * If you want to get the headers and data for each redirect, set the number of + * redirects to 0 and perform a new OFHTTPRequest for each redirect. However, + * this callback will not be called then and you have to look at the status code + * to detect a redirect. + * + * This callback will only be called if the OFHTTPRequest will follow a + * redirect. If the maximum number of redirects has been reached already, this + * callback will not be called. + * + * \param request The OFHTTPRequest which will follow a redirect + * \param url The URL to which it will follow a redirect + * \return A boolean whether the OFHTTPRequest should follow the redirect + */ +- (BOOL)request: (OFHTTPRequest*)request + willFollowRedirectTo: (OFURL*)url; +@end + /** * \brief A class for storing and performing HTTP requests. */ @interface OFHTTPRequest: OFObject { @@ -36,18 +92,20 @@ OFURL *URL; of_http_request_type_t requestType; OFString *queryString; OFDictionary *headers; BOOL redirectsFromHTTPSToHTTPAllowed; + id delegate; } #ifdef OF_HAVE_PROPERTIES @property (copy) OFURL *URL; @property (assign) of_http_request_type_t requestType; @property (copy) OFString *queryString; @property (copy) OFDictionary *headers; @property (assign) BOOL redirectsFromHTTPSToHTTPAllowed; +@property (retain) id delegate; #endif /** * \return A new, autoreleased OFHTTPRequest */ @@ -125,10 +183,22 @@ /** * \return Whether redirects from HTTPS to HTTP are allowed */ - (BOOL)redirectsFromHTTPSToHTTPAllowed; +/** + * Sets the delegate for the HTTP request. + * + * \param delegate The delegate for the HTTP request + */ +- (void)setDelegate: (id )delegate; + +/** + * \return The delegate for the HTTP request. + */ +- (id )delegate; + /** * Performs the HTTP request and returns an OFHTTPRequestResult. * * \return An OFHTTPRequestResult with the result of the HTTP request */ @@ -180,7 +250,10 @@ /** * \return The data returned for the HTTP request */ - (OFDataArray*)data; @end + +@interface OFObject (OFHTTPRequestDelegate) +@end extern Class of_http_request_tls_socket_class; Index: src/OFHTTPRequest.m ================================================================== --- src/OFHTTPRequest.m +++ src/OFHTTPRequest.m @@ -14,17 +14,20 @@ * file. */ #include "config.h" +#define OF_HTTP_REQUEST_M + #include #import "OFHTTPRequest.h" #import "OFString.h" #import "OFURL.h" #import "OFTCPSocket.h" #import "OFDictionary.h" +#import "OFDataArray.h" #import "OFAutoreleasePool.h" #import "OFHTTPRequestFailedException.h" #import "OFInvalidServerReplyException.h" #import "OFOutOfRangeException.h" @@ -129,10 +132,20 @@ - (BOOL)redirectsFromHTTPSToHTTPAllowed { return redirectsFromHTTPSToHTTPAllowed; } + +- (void)setDelegate: (id )delegate_ +{ + OF_SETTER(delegate, delegate_, YES, NO) +} + +- (id )delegate +{ + OF_GETTER(delegate, YES) +} - (OFHTTPRequestResult*)perform { return [self performWithRedirects: 10]; } @@ -166,10 +179,11 @@ OFDataArray *data; OFEnumerator *enumerator; OFString *key; int status; const char *t = NULL; + char *buf; [sock connectToHost: [URL host] onPort: [URL port]]; /* @@ -272,13 +286,25 @@ @"Location"] == OF_ORDERED_SAME) && (redirectsFromHTTPSToHTTPAllowed || [scheme isEqual: @"http"] || ![value hasPrefix: @"http://"])) { OFURL *new; + BOOL follow; + + new = [OFURL URLWithString: value + relativeToURL: URL]; + + follow = [delegate request: self + willFollowRedirectTo: new]; + + if (!follow && delegate != nil) { + [s_headers setObject: value + forKey: key]; + continue; + } - new = [[OFURL alloc] initWithString: value - relativeToURL: URL]; + new = [new retain]; [URL release]; URL = new; if (status == 303) { requestType = OF_HTTP_REQUEST_TYPE_GET; @@ -295,11 +321,30 @@ [s_headers setObject: value forKey: key]; } - data = [sock readDataArrayTillEndOfStream]; + [delegate request: self + didReceiveHeaders: s_headers + withStatusCode: status]; + + data = [OFDataArray dataArrayWithItemSize: 1]; + buf = [self allocMemoryWithSize: of_pagesize]; + @try { + size_t len; + + while ((len = [sock readNBytes: of_pagesize + intoBuffer: buf]) > 0) { + [data addNItems: len + fromCArray: buf]; + [delegate request: self + didReceiveData: buf + withLength: len]; + } + } @finally { + [self freeMemory: buf]; + } if ([s_headers objectForKey: @"Content-Length"] != nil) { intmax_t cl; cl = [[s_headers objectForKey: @"Content-Length"] @@ -369,5 +414,25 @@ - (OFDataArray*)data { return [[data retain] autorelease]; } @end + +@implementation OFObject (OFHTTPRequestDelegate) +- (void)request: (OFHTTPRequest*)request + didReceiveHeaders: (OFDictionary*)headers + withStatusCode: (int)statusCode +{ +} + +- (void)request: (OFHTTPRequest*)request + didReceiveData: (const char*)data + withLength: (size_t)len +{ +} + +- (BOOL)request: (OFHTTPRequest*)request + willFollowRedirectTo: (OFURL*)url +{ + return YES; +} +@end