tweaks for url loading

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@25142 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2007-05-11 15:47:06 +00:00
parent 7c633ab823
commit 1d562005d1
9 changed files with 259 additions and 240 deletions

View file

@ -5,9 +5,11 @@
* Source/win32/NSStreamWin32.m: * Source/win32/NSStreamWin32.m:
* Headers/Foundation/NSStream.h: * Headers/Foundation/NSStream.h:
First hack at extensions to get address and port properties for First hack at extensions to get address and port properties for
network streams. network streams. Use localhost if no host is given for connect.
* Source/GSFFIInvocation.m: * Source/GSFFIInvocation.m:
Attempt to use forward2 if available. Attempt to use forward2 if available.
* Source/NSURLProtocol.m:
Fixup some problems with incorporation of code from mySTEP.
2007-05-11 Richard Frith-Macdonald <rfm@gnu.org> 2007-05-11 Richard Frith-Macdonald <rfm@gnu.org>

View file

@ -442,6 +442,7 @@ extern "C" {
#include <Foundation/NSURLHandle.h> #include <Foundation/NSURLHandle.h>
#include <Foundation/NSURLConnection.h>
@class NSArray; @class NSArray;
@class NSDictionary; @class NSDictionary;
@ -504,13 +505,10 @@ extern "C" {
@interface GSXMLRPC : NSObject <NSURLHandleClient> @interface GSXMLRPC : NSObject <NSURLHandleClient>
{ {
@private @private
#ifdef GNUSTEP
NSURLHandle *handle; NSURLHandle *handle;
#else
NSString *connectionURL; NSString *connectionURL;
NSURLConnection *connection; NSURLConnection *connection;
NSMutableData *response; NSMutableData *response;
#endif
NSTimer *timer; NSTimer *timer;
id result; id result;
id delegate; // Not retained. id delegate; // Not retained.

View file

@ -72,7 +72,7 @@ extern "C" {
/** <init /> /** <init />
* Initialises the receiver with the specified request (performing * Initialises the receiver with the specified request (performing
* a deep copy so that ithe request does not change during loading) * a deep copy so that the request does not change during loading)
* and delegate.<br /> * and delegate.<br />
* This automatically initiates an asynchronous load for the request.<br /> * This automatically initiates an asynchronous load for the request.<br />
* Processing of the request is done in the thread which calls this * Processing of the request is done in the thread which calls this

View file

@ -52,29 +52,7 @@
#include "GNUstepBase/GSMime.h" #include "GNUstepBase/GSMime.h"
#include "GNUstepBase/GSXML.h" #include "GNUstepBase/GSXML.h"
#ifndef NeXT_Foundation_LIBRARY
#include <Foundation/NSArray.h>
#include <Foundation/NSBundle.h>
#include <Foundation/NSCalendarDate.h>
#include <Foundation/NSCharacterSet.h>
#include <Foundation/NSData.h>
#include <Foundation/NSDate.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSException.h>
#include <Foundation/NSFileManager.h>
#include <Foundation/NSHashTable.h>
#include <Foundation/NSInvocation.h>
#include <Foundation/NSMapTable.h>
#include <Foundation/NSRunLoop.h>
#include <Foundation/NSString.h>
#include <Foundation/NSTimeZone.h>
#include <Foundation/NSTimer.h>
#include <Foundation/NSURL.h>
#include <Foundation/NSURLHandle.h>
#include <Foundation/NSValue.h>
#else
#include <Foundation/Foundation.h> #include <Foundation/Foundation.h>
#endif
/* libxml headers */ /* libxml headers */
#include <libxml/tree.h> #include <libxml/tree.h>

View file

@ -66,7 +66,7 @@
- (void) _setHeaders: (id)headers; - (void) _setHeaders: (id)headers;
- (void) _setStatusCode: (int)code text: (NSString*)text; - (void) _setStatusCode: (int)code text: (NSString*)text;
- (void) _setValue: (NSString *)value forHTTPHeaderField: (NSString *)field; - (void) _setValue: (NSString *)value forHTTPHeaderField: (NSString *)field;
- (NSString *) _valueForHTTPHeaderField: (NSString *)field; - (NSString*) _valueForHTTPHeaderField: (NSString*)field;
@end @end

View file

@ -74,16 +74,27 @@ typedef struct {
static NSMutableArray *registered = nil; static NSMutableArray *registered = nil;
static NSLock *regLock = nil; static NSLock *regLock = nil;
static Class abstractClass = nil;
static NSURLProtocol *placeholder = nil;
@implementation NSURLProtocol @implementation NSURLProtocol
+ (id) allocWithZone: (NSZone*)z + (id) allocWithZone: (NSZone*)z
{ {
NSURLProtocol *o = [super allocWithZone: z]; NSURLProtocol *o;
if (o != nil) if ((self == abstractClass) && (z == 0 || z == NSDefaultMallocZone()))
{ {
o->_NSURLProtocolInternal = NSZoneCalloc(z, 1, sizeof(Internal)); /* Return a default placeholder instance to avoid the overhead of
* creating and destroying instances of the abstract class.
*/
o = placeholder;
}
else
{
/* Create and return an instance of the concrete subclass.
*/
o = (NSURLProtocol*)NSAllocateObject(self, 0, z);
} }
return o; return o;
} }
@ -92,6 +103,9 @@ static NSLock *regLock = nil;
{ {
if (registered == nil) if (registered == nil)
{ {
abstractClass = [NSURLProtocol class];
placeholder = (NSURLProtocol*)NSAllocateObject(abstractClass, 0,
NSDefaultMallocZone());
registered = [NSMutableArray new]; registered = [NSMutableArray new];
regLock = [NSLock new]; regLock = [NSLock new];
[self registerClass: [_NSHTTPURLProtocol class]]; [self registerClass: [_NSHTTPURLProtocol class]];
@ -145,6 +159,11 @@ static NSLock *regLock = nil;
- (void) dealloc - (void) dealloc
{ {
if (self == placeholder)
{
[self retain];
return;
}
if (this != 0) if (this != 0)
{ {
[self stopLoading]; [self stopLoading];
@ -153,6 +172,7 @@ static NSLock *regLock = nil;
RELEASE(this->cachedResponse); RELEASE(this->cachedResponse);
RELEASE(this->request); RELEASE(this->request);
NSZoneFree([self zone], this); NSZoneFree([self zone], this);
_NSURLProtocolInternal = 0;
} }
[super dealloc]; [super dealloc];
} }
@ -160,14 +180,27 @@ static NSLock *regLock = nil;
- (NSString*) description - (NSString*) description
{ {
return [NSString stringWithFormat:@"%@ %@", return [NSString stringWithFormat:@"%@ %@",
[super description], this->request]; [super description], this ? (id)this->request : nil];
}
- (id) init
{
if ((self = [super init]) != nil)
{
if (isa != abstractClass)
{
_NSURLProtocolInternal = NSZoneCalloc(GSObjCZone(self),
1, sizeof(Internal));
}
}
return self;
} }
- (id) initWithRequest: (NSURLRequest *)request - (id) initWithRequest: (NSURLRequest *)request
cachedResponse: (NSCachedURLResponse *)cachedResponse cachedResponse: (NSCachedURLResponse *)cachedResponse
client: (id <NSURLProtocolClient>)client client: (id <NSURLProtocolClient>)client
{ {
if (isa == [NSURLProtocol class]) if (isa == abstractClass)
{ {
unsigned count; unsigned count;
@ -189,7 +222,7 @@ static NSLock *regLock = nil;
cachedResponse: cachedResponse cachedResponse: cachedResponse
client: client]; client: client];
} }
if ((self = [super init]) != nil) if ((self = [self init]) != nil)
{ {
this->request = [request copy]; this->request = [request copy];
this->cachedResponse = RETAIN(cachedResponse); this->cachedResponse = RETAIN(cachedResponse);
@ -390,82 +423,83 @@ static NSLock *regLock = nil;
{ // process header line { // process header line
unsigned char *c, *end; unsigned char *c, *end;
NSString *key, *val; NSString *key, *val;
#if 0 #if 1
NSLog(@"process header line len=%d", len); NSLog(@"process header line len=%d", len);
#endif #endif
// if it begins with ' ' or '\t' it is a continuation line to the previous header field // if it begins with ' ' or '\t' it is a continuation line to the previous header field
if (!_headers) if (!_headers)
{ // should be/must be the header line { // should be/must be the header line
unsigned major, minor; unsigned major, minor;
if (sscanf((char *) buffer, "HTTP/%u.%u %u", &major, &minor, &_statusCode) == 3) if (sscanf((char *) buffer, "HTTP/%u.%u %u", &major, &minor, &_statusCode) == 3)
{ // response header line { // response header line
if (major != 1 || minor > 1) if (major != 1 || minor > 1)
[this->client URLProtocol: self didFailWithError: [NSError errorWithDomain: @"Bad HTTP version" code: 0 userInfo: nil]]; [this->client URLProtocol: self didFailWithError: [NSError errorWithDomain: @"Bad HTTP version" code: 0 userInfo: nil]];
// must be first - but must also be present and valid before we go to receive the body! // must be first - but must also be present and valid before we go to receive the body!
_headers=[NSMutableDictionary dictionaryWithCapacity: 10]; // start collecting headers _headers=[NSMutableDictionary dictionaryWithCapacity: 10]; // start collecting headers
// if (_statusCode >= 400 && _statusCode <= 499) // if (_statusCode >= 400 && _statusCode <= 499)
NSLog(@"Client header: %.*s", len, buffer); NSLog(@"Client header: %.*s", len, buffer);
return NO; // process next line return NO; // process next line
} }
else else
; // invalid header ; // invalid header
return NO; // process next line return NO; // process next line
} }
if (len == 0) if (len == 0)
{ // empty line, i.e. end of header { // empty line, i.e. end of header
NSString *loc; NSString *loc;
NSHTTPURLResponse *response; NSHTTPURLResponse *response;
response = [[NSHTTPURLResponse alloc] initWithURL: [this->request URL] response = [[NSHTTPURLResponse alloc] initWithURL: [this->request URL]
MIMEType: nil MIMEType: nil
expectedContentLength: -1 expectedContentLength: -1
textEncodingName: nil]; textEncodingName: nil];
[response _setHeaders: _headers]; [response _setHeaders: _headers];
DESTROY(_headers); DESTROY(_headers);
[response _setStatusCode: _statusCode text: @""]; [response _setStatusCode: _statusCode text: @""];
loc = [response _valueForHTTPHeaderField: @"location"]; loc = [response _valueForHTTPHeaderField: @"location"];
if ([loc length]) if ([loc length])
{ // Location: entry exists { // Location: entry exists
NSURLRequest *request=[NSURLRequest requestWithURL: [NSURL URLWithString: loc]]; NSURLRequest *request=[NSURLRequest requestWithURL: [NSURL URLWithString: loc]];
if (!request) if (!request)
[this->client URLProtocol: self didFailWithError: [NSError errorWithDomain: @"Invalid redirect request" code: 0 userInfo: nil]]; // error [this->client URLProtocol: self didFailWithError: [NSError errorWithDomain: @"Invalid redirect request" code: 0 userInfo: nil]]; // error
[this->client URLProtocol: self wasRedirectedToRequest: request redirectResponse: response]; [this->client URLProtocol: self wasRedirectedToRequest: request redirectResponse: response];
} }
else else
{ {
NSURLCacheStoragePolicy policy=NSURLCacheStorageAllowed; // default NSURLCacheStoragePolicy policy=NSURLCacheStorageAllowed; // default
// read from [this->request cachePolicy]; // read from [this->request cachePolicy];
/* /*
NSURLCacheStorageAllowed, NSURLCacheStorageAllowed,
NSURLCacheStorageAllowedInMemoryOnly NSURLCacheStorageAllowedInMemoryOnly
NSURLCacheStorageNotAllowed NSURLCacheStorageNotAllowed
*/ */
if ([self isKindOfClass: [_NSHTTPSURLProtocol class]]) if ([self isKindOfClass: [_NSHTTPSURLProtocol class]])
policy=NSURLCacheStorageNotAllowed; // never policy=NSURLCacheStorageNotAllowed; // never
[this->client URLProtocol: self didReceiveResponse: response cacheStoragePolicy: policy]; NSLog(@"Received");
} [this->client URLProtocol: self didReceiveResponse: response cacheStoragePolicy: policy];
return YES; }
} return YES;
}
for (c=buffer, end=c+len; *c != ':'; c++) for (c=buffer, end=c+len; *c != ':'; c++)
{ {
if (c == end) if (c == end)
{ // no colon found! { // no colon found!
// raise bad header error or simply ignore? // raise bad header error or simply ignore?
return NO; // keep processing header lines return NO; // keep processing header lines
} }
} }
key=[[NSString stringWithCString: (char *) buffer length: c-buffer] capitalizedString]; key=[[NSString stringWithCString: (char *) buffer length: c-buffer] capitalizedString];
while(++c < end && (*c == ' ' || *c == '\t')) while(++c < end && (*c == ' ' || *c == '\t'))
; // skip spaces ; // skip spaces
val=[NSString stringWithCString: (char *) c length: end-c]; val=[NSString stringWithCString: (char *) c length: end-c];
[_headers setObject: val forKey: [key lowercaseString]]; [_headers setObject: val forKey: [key lowercaseString]];
return NO; // not yet done return NO; // not yet done
} }
- (void) _processHeader: (unsigned char *) buffer length: (int) len - (void) _processHeader: (unsigned char *) buffer length: (int) len
{ // next header fragment received { // next header fragment received
unsigned char *ptr, *end; unsigned char *ptr, *end;
#if 0 #if 1
NSLog(@"received %d bytes", len); NSLog(@"received %d bytes", len);
#endif #endif
if (len <= 0) if (len <= 0)
@ -534,147 +568,156 @@ static NSLock *regLock = nil;
#if 0 #if 0
NSLog(@"stream: %@ handleEvent: %x for: %@", stream, event, self); NSLog(@"stream: %@ handleEvent: %x for: %@", stream, event, self);
#endif #endif
if (stream == this->input) if (stream == this->input)
{ {
switch(event)
{
case NSStreamEventHasBytesAvailable:
{
unsigned char buffer[512];
int len=[(NSInputStream *) stream read: buffer maxLength: sizeof(buffer)];
if (len < 0)
{
#if 1 #if 1
NSLog(@"receive error %@", [NSError _last]); NSLog(@"input stream handleEvent: %x for: %@", event, self);
#endif #endif
[this->client URLProtocol: self didFailWithError: [NSError errorWithDomain: @"receive error" code: 0 userInfo: nil]]; switch(event)
[self _unschedule]; {
return; case NSStreamEventHasBytesAvailable:
} {
if (_readingBody) unsigned char buffer[512];
[this->client URLProtocol: self didLoadData: [NSData dataWithBytes: buffer length: len]]; // notify int len;
else
[self _processHeader: buffer length: len]; len = [(NSInputStream *)stream read: buffer
return; maxLength: sizeof(buffer)];
} if (len < 0)
case NSStreamEventEndEncountered: // can this occur in parallel to NSStreamEventHasBytesAvailable??? {
{ #if 1
#if 0 NSLog(@"receive error %@", [NSError _last]);
NSLog(@"end of response"); #endif
#endif [this->client URLProtocol: self didFailWithError: [NSError errorWithDomain: @"receive error" code: 0 userInfo: nil]];
if (!_readingBody) [self _unschedule];
[this->client URLProtocol: self didFailWithError: [NSError errorWithDomain: @"incomplete header" code: 0 userInfo: nil]]; return;
[this->client URLProtocolDidFinishLoading: self]; }
_readingBody=NO; if (_readingBody)
[self _unschedule]; [this->client URLProtocol: self didLoadData: [NSData dataWithBytes: buffer length: len]]; // notify
return; else
} [self _processHeader: buffer length: len];
case NSStreamEventOpenCompleted: return;
{ // prepare to receive header }
#if 0 case NSStreamEventEndEncountered: // can this occur in parallel to NSStreamEventHasBytesAvailable???
NSLog(@"HTTP input stream opened"); {
#endif #if 1
return; NSLog(@"end of response");
} #endif
default: if (!_readingBody)
break; [this->client URLProtocol: self didFailWithError: [NSError errorWithDomain: @"incomplete header" code: 0 userInfo: nil]];
} [this->client URLProtocolDidFinishLoading: self];
} _readingBody=NO;
[self _unschedule];
return;
}
case NSStreamEventOpenCompleted:
{ // prepare to receive header
#if 1
NSLog(@"HTTP input stream opened");
#endif
return;
}
default:
break;
}
}
else if (stream == this->output) else if (stream == this->output)
{ {
unsigned char *msg; unsigned char *msg;
#if 0
NSLog(@"An event occurred on the output stream."); #if 0
#endif NSLog(@"An event occurred on the output stream.");
/* e.g. #endif
POST /wiki/Spezial: Search HTTP/1.1 /* e.g.
Host: de.wikipedia.org POST /wiki/Spezial: Search HTTP/1.1
Content-Type: application/x-www-form-urlencoded Host: de.wikipedia.org
Content-Length: 24 Content-Type: application/x-www-form-urlencoded
Content-Length: 24
search=Katzen&go=Artikel <- body
*/ search=Katzen&go=Artikel <- body
switch(event) */
{
case NSStreamEventOpenCompleted: switch(event)
{ {
#if 0 case NSStreamEventOpenCompleted:
NSLog(@"HTTP output stream opened"); {
#endif #if 0
msg=(unsigned char *) [[NSString stringWithFormat: @"%@ %@ HTTP/1.1\r\n", NSLog(@"HTTP output stream opened");
[this->request HTTPMethod], #endif
[[this->request URL] absoluteString] msg = (unsigned char *)[[NSString stringWithFormat:
] cString]; // FIXME: UTF8??? @"%@ %@ HTTP/1.1\r\n",
[(NSOutputStream *) stream write: msg maxLength: strlen((char *) msg)]; [this->request HTTPMethod],
#if 1 [[this->request URL] absoluteString]] UTF8String];
NSLog(@"sent %s", msg); [(NSOutputStream *) stream write: msg
#endif maxLength: strlen((char *) msg)];
_headerEnumerator=[[[this->request allHTTPHeaderFields] objectEnumerator] retain]; #if 0
return; NSLog(@"sent %s", msg);
} #endif
case NSStreamEventHasSpaceAvailable: _headerEnumerator = [[[this->request allHTTPHeaderFields] keyEnumerator] retain];
{ return;
// FIXME: should also send out relevant Cookies }
if (_headerEnumerator) case NSStreamEventHasSpaceAvailable:
{ // send next header {
NSString *key; // FIXME: should also send out relevant Cookies
key=[_headerEnumerator nextObject]; if (_headerEnumerator)
if (key) { // send next header
{ NSString *key;
#if 1 key = [_headerEnumerator nextObject];
NSLog(@"sending %@: %@", key, [this->request valueForHTTPHeaderField: key]); if (key)
#endif {
msg=(unsigned char *)[[NSString stringWithFormat: @"%@: %@\r\n", key, [this->request valueForHTTPHeaderField: key]] UTF8String]; #if 0
} NSLog(@"sending %@: %@", key, [this->request valueForHTTPHeaderField: key]);
else #endif
{ // was last header entry msg=(unsigned char *)[[NSString stringWithFormat: @"%@: %@\r\n", key, [this->request valueForHTTPHeaderField: key]] UTF8String];
[_headerEnumerator release]; }
_headerEnumerator=nil; else
msg=(unsigned char *) "\r\n"; // send empty line { // was last header entry
_body=[[this->request HTTPBodyStream] retain]; // if present [_headerEnumerator release];
if (!_body && [this->request HTTPBody]) _headerEnumerator=nil;
_body=[[NSInputStream alloc] initWithData: [this->request HTTPBody]]; // prepare to send request body msg=(unsigned char *) "\r\n"; // send empty line
[_body open]; _body=[[this->request HTTPBodyStream] retain]; // if present
} if (!_body && [this->request HTTPBody])
[(NSOutputStream *) stream write: msg maxLength: strlen((char *) msg)]; // NOTE: we might block here if header value is too long _body=[[NSInputStream alloc] initWithData: [this->request HTTPBody]]; // prepare to send request body
#if 1 [_body open];
NSLog(@"sent %s", msg); }
#endif [(NSOutputStream *) stream write: msg maxLength: strlen((char *) msg)]; // NOTE: we might block here if header value is too long
return; #if 0
} NSLog(@"sent %s", msg);
else if (_body) #endif
{ // send (next part of) body until done return;
if ([_body hasBytesAvailable]) }
{ else if (_body)
unsigned char buffer[512]; { // send (next part of) body until done
int len=[_body read: buffer maxLength: sizeof(buffer)]; // read next block from stream if ([_body hasBytesAvailable])
if (len < 0) {
{ unsigned char buffer[512];
#if 1 int len=[_body read: buffer maxLength: sizeof(buffer)]; // read next block from stream
NSLog(@"error reading from HTTPBody stream %@", [NSError _last]); if (len < 0)
#endif {
[self _unschedule]; #if 1
return; NSLog(@"error reading from HTTPBody stream %@", [NSError _last]);
} #endif
[(NSOutputStream *) stream write: buffer maxLength: len]; // send [self _unschedule];
} return;
else }
{ // done [(NSOutputStream *) stream write: buffer maxLength: len]; // send
#if 0 }
NSLog(@"request sent"); else
#endif { // done
[self _unschedule]; // well, we should just unschedule the send stream #if 0
[_body close]; NSLog(@"request sent");
[_body release]; #endif
_body=nil; [self _unschedule]; // well, we should just unschedule the send stream
} [_body close];
} [_body release];
return; // done _body=nil;
} }
default: }
break; return; // done
} }
} default:
break;
}
}
NSLog(@"An error %@ occurred on the event %08x of stream %@ of %@", [stream streamError], event, stream, self); NSLog(@"An error %@ occurred on the event %08x of stream %@ of %@", [stream streamError], event, stream, self);
[this->client URLProtocol: self didFailWithError: [stream streamError]]; [this->client URLProtocol: self didFailWithError: [stream streamError]];
} }

View file

@ -399,6 +399,5 @@ static const NSMapTableKeyCallBacks headerKeyCallBacks =
{ {
return this->statusCode; return this->statusCode;
} }
@end @end

View file

@ -1268,7 +1268,7 @@ static void setNonblocking(int fd)
inputStream: (NSInputStream **)inputStream inputStream: (NSInputStream **)inputStream
outputStream: (NSOutputStream **)outputStream outputStream: (NSOutputStream **)outputStream
{ {
NSString *address = [host address]; NSString *address = host ? (id)[host address] : (id)@"127.0.0.1";
GSSocketInputStream *ins = nil; GSSocketInputStream *ins = nil;
GSSocketOutputStream *outs = nil; GSSocketOutputStream *outs = nil;
int sock; int sock;
@ -1281,10 +1281,10 @@ static void setNonblocking(int fd)
if (!ins) if (!ins)
{ {
#if defined(PF_INET6) #if defined(PF_INET6)
ins = [[GSInet6InputStream alloc] initToAddr: address ins = AUTORELEASE([[GSInet6InputStream alloc]
port: port]; initToAddr: address port: port]);
outs = [[GSInet6OutputStream alloc] initToAddr: address outs = AUTORELEASE([[GSInet6OutputStream alloc]
port: port]; initToAddr: address port: port]);
sock = socket(PF_INET6, SOCK_STREAM, 0); sock = socket(PF_INET6, SOCK_STREAM, 0);
#else #else
sock = -1; sock = -1;
@ -1308,7 +1308,6 @@ static void setNonblocking(int fd)
[outs setSibling: ins]; [outs setSibling: ins];
*outputStream = outs; *outputStream = outs;
} }
return;
} }
+ (void) getLocalStreamsToPath: (NSString *)path + (void) getLocalStreamsToPath: (NSString *)path

View file

@ -1926,7 +1926,7 @@ static id propertyForInet6Stream(int descriptor, NSString *key)
inputStream: (NSInputStream **)inputStream inputStream: (NSInputStream **)inputStream
outputStream: (NSOutputStream **)outputStream outputStream: (NSOutputStream **)outputStream
{ {
NSString *address = [host address]; NSString *address = host ? (id)[host address] : (id)@"127.0.0.1";
GSSocketInputStream *ins = nil; GSSocketInputStream *ins = nil;
GSSocketOutputStream *outs = nil; GSSocketOutputStream *outs = nil;
int sock; int sock;