26
26
#include "config.h"
27
27
#include "Foundation/NSArray.h"
28
#include "Foundation/NSString.h"
28
#include "Foundation/NSByteOrder.h"
29
#include "Foundation/NSData.h"
30
#include "Foundation/NSDebug.h"
29
31
#include "Foundation/NSException.h"
30
#include "Foundation/NSValue.h"
31
#include "Foundation/NSData.h"
32
#include "Foundation/NSURL.h"
33
#include "Foundation/NSURLHandle.h"
34
#include "Foundation/NSNotification.h"
35
#include "Foundation/NSRunLoop.h"
36
#include "Foundation/NSByteOrder.h"
37
#include "Foundation/NSLock.h"
38
32
#include "Foundation/NSFileHandle.h"
39
#include "Foundation/NSDebug.h"
40
33
#include "Foundation/NSHost.h"
34
#include "Foundation/NSLock.h"
35
#include "Foundation/NSMapTable.h"
36
#include "Foundation/NSNotification.h"
37
#include "Foundation/NSPathUtilities.h"
41
38
#include "Foundation/NSProcessInfo.h"
42
#include "Foundation/NSPathUtilities.h"
43
#include "Foundation/NSMapTable.h"
39
#include "Foundation/NSRunLoop.h"
40
#include "Foundation/NSString.h"
41
#include "Foundation/NSURL.h"
42
#include "Foundation/NSURLHandle.h"
43
#include "Foundation/NSValue.h"
44
44
#include "GNUstepBase/GSMime.h"
45
45
#include "GNUstepBase/GSLock.h"
46
46
#include "NSCallBacks.h"
47
47
#include "GSURLPrivate.h"
48
#include "GSPrivate.h"
49
50
#include <string.h>
50
51
#ifdef HAVE_UNISTD_H
55
56
#ifdef HAVE_SYS_FCNTL_H
56
57
#include <sys/fcntl.h> // For O_WRONLY, etc
59
#ifdef HAVE_SYS_SOCKET_H
60
#include <sys/socket.h> // For MSG_PEEK, etc
60
64
* Implement map keys for strings with case insensitive comparisons,
81
85
typedef void (*NSMT_release_func_t)(NSMapTable *, void *);
82
86
typedef NSString *(*NSMT_describe_func_t)(NSMapTable *, const void *);
84
const NSMapTableKeyCallBacks writeKeyCallBacks =
88
static const NSMapTableKeyCallBacks writeKeyCallBacks =
86
90
(NSMT_hash_func_t) _non_retained_id_hash,
87
91
(NSMT_is_equal_func_t) _non_retained_id_is_equal,
211
215
@implementation GSHTTPURLHandle
217
#define MAX_CACHED 16
213
219
static NSMutableDictionary *urlCache = nil;
220
static NSMutableArray *urlOrder = nil;
214
221
static NSLock *urlLock = nil;
216
223
static Class sslClass = 0;
278
285
//NSLog(@"Lookup for handle for '%@'", page);
280
287
obj = [urlCache objectForKey: page];
281
AUTORELEASE(RETAIN(obj));
290
[urlOrder removeObjectIdenticalTo: obj];
291
[urlOrder addObject: obj];
292
AUTORELEASE(RETAIN(obj));
282
294
[urlLock unlock];
283
295
//NSLog(@"Found handle %@", obj);
290
302
if (self == [GSHTTPURLHandle class])
292
304
urlCache = [NSMutableDictionary new];
305
urlOrder = [NSMutableArray new];
293
306
urlLock = [GSLazyLock new];
294
307
debugLock = [GSLazyLock new];
295
308
debugFile = [NSString stringWithFormat: @"%@/GSHTTP.%d",
310
323
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
313
* We might be in an idle state with an outstandng read on the
314
* socket, keeping the connection alive, but waiting for the
315
* remote end to drop it.
317
[nc removeObserver: self
318
name: NSFileHandleReadCompletionNotification
325
[nc removeObserver: self name: nil object: sock];
320
326
[sock closeFile];
350
356
connectionState = idle;
351
357
if (cached == YES)
353
NSString *page = [newUrl absoluteString];
359
NSString *page = [newUrl absoluteString];
360
GSHTTPURLHandle *obj;
363
obj = [urlCache objectForKey: page];
356
364
[urlCache setObject: self forKey: page];
367
[urlOrder removeObjectIdenticalTo: obj];
369
[urlOrder addObject: self];
370
while ([urlOrder count] > MAX_CACHED)
372
obj = [urlOrder objectAtIndex: 0];
373
[urlCache removeObjectForKey: [obj->url absoluteString]];
374
[urlOrder removeObjectAtIndex: 0];
357
376
[urlLock unlock];
358
377
//NSLog(@"Cache handle %@ for '%@'", self, page);
433
452
NSURLCredential *cred;
434
453
NSString *method;
437
* Create credential from user and password
455
/* Create credential from user and password stored in the URL.
456
* Returns nil if we have no username or password.
440
458
cred = [[NSURLCredential alloc]
441
459
initWithUser: [u user]
442
460
password: [u password]
443
461
persistence: NSURLCredentialPersistenceForSession];
445
authentication = [GSHTTPAuthentication
446
authenticationWithCredential: cred
447
inProtectionSpace: space];
465
authentication = nil;
469
/* Create authentication from credential ... returns nil if
470
* we have no credential.
472
authentication = [GSHTTPAuthentication
473
authenticationWithCredential: cred
474
inProtectionSpace: space];
451
478
method = [request objectForKey: GSHTTPPropertyMethodKey];
452
479
if (method == nil)
464
491
auth = [authentication authorizationForAuthentication: nil
468
NSMapInsert(wProperties, (void*)@"Authorization", (void*)auth);
494
/* If authentication is nil then auth will also be nil
498
[self writeProperty: auth forKey: @"Authorization"];
519
551
if (debug) NSLog(@"%@ %s", NSStringFromSelector(_cmd), keepalive?"K":"");
520
552
d = [dict objectForKey: NSFileHandleNotificationDataItem];
521
553
if (debug == YES) debugRead(self, d);
554
readCount = [d length];
523
556
if (connectionState == idle)
530
563
if (debug == YES && [d length] != 0)
532
NSLog(@"%@ %s Unexpected data from remote!",
533
NSStringFromSelector(_cmd), keepalive?"K":"");
565
NSLog(@"%@ %s Unexpected data (%*.*s) from remote!",
566
NSStringFromSelector(_cmd), keepalive?"K":"",
567
[d length], [d length], [d bytes]);
535
[nc removeObserver: self
536
name: NSFileHandleReadCompletionNotification
569
[nc removeObserver: self name: nil object: sock];
538
570
[sock closeFile];
585
617
GSMimeHeader *info;
589
622
connectionState = idle;
623
[nc removeObserver: self name: nil object: sock];
591
625
ver = [[[document headerNamed: @"http"] value] floatValue];
592
626
val = [[document headerNamed: @"connection"] value];
593
627
if (ver < 1.1 || (val != nil && [val isEqual: @"close"] == YES))
595
[nc removeObserver: self
596
name: NSFileHandleReadCompletionNotification
629
[nc removeObserver: self name: nil object: sock];
598
630
[sock closeFile];
614
647
NSURLProtectionSpace *space;
616
NSURLCredential *cred;
617
649
GSHTTPAuthentication *authentication;
618
650
NSString *method;
622
654
space = [GSHTTPAuthentication
623
655
protectionSpaceForAuthentication: ac requestURL: url];
626
* Create credential from user and password
629
cred = [[NSURLCredential alloc]
630
initWithUser: [url user]
631
password: [url password]
632
persistence: NSURLCredentialPersistenceForSession];
635
* Get the digest object and ask it for a header
636
* to use for authorisation.
638
authentication = [GSHTTPAuthentication
639
authenticationWithCredential: cred
640
inProtectionSpace: space];
658
authentication = nil;
662
NSURLCredential *cred;
665
* Create credential from user and password
667
* Returns nil if we have no username or password.
669
cred = [[NSURLCredential alloc]
670
initWithUser: [url user]
671
password: [url password]
672
persistence: NSURLCredentialPersistenceForSession];
676
authentication = nil;
681
* Get the digest object and ask it for a header
682
* to use for authorisation.
683
* Returns nil if we have no credential.
685
authentication = [GSHTTPAuthentication
686
authenticationWithCredential: cred
687
inProtectionSpace: space];
644
692
method = [request objectForKey: GSHTTPPropertyMethodKey];
645
693
if (method == nil)
657
a = [authentication authorizationForAuthentication: ac
705
auth = [authentication authorizationForAuthentication: ac
662
[self writeProperty: a forKey: @"Authorization"];
710
[self writeProperty: auth forKey: @"Authorization"];
663
711
[self _tryLoadInBackground: u];
664
712
return; // Retrying.
690
738
NSResetMapTable(wProperties);
691
[self didLoadBytes: [d subdataWithRange: r]
739
if (code >= 200 && code < 300)
741
[self didLoadBytes: [d subdataWithRange: r]
746
[self didLoadBytes: [d subdataWithRange: r]
748
[self cancelLoadInBackground];
705
762
loadComplete: NO];
709
&& (connectionState == reading || connectionState == idle))
766
if (complete == NO && readCount == 0)
768
/* The read failed ... dropped, but parsing is not complete.
769
* The request was sent, so we can't know whether it was
770
* lost in the network or the remote end received it and
771
* the response was lost.
775
NSLog(@"HTTP response not received - %@", parser);
777
[self endLoadInBackground];
778
[self backgroundLoadDidFailWithReason: @"Response parse failed"];
781
if (sock != nil && connectionState == reading)
711
783
if ([sock readInProgress] == NO)
780
852
if (connectionState != idle)
782
854
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
785
if (connectionState == connecting)
786
name = GSFileHandleConnectCompletionNotification;
787
else if (connectionState == writing)
788
name = GSFileHandleWriteCompletionNotification;
790
name = NSFileHandleReadCompletionNotification;
792
[nc removeObserver: self name: name object: sock];
856
[nc removeObserver: self name: nil object: sock];
793
857
[sock closeFile];
795
859
connectionState = idle;
989
1053
* then we may try again with a new connection.
991
1055
nc = [NSNotificationCenter defaultCenter];
992
[nc removeObserver: self
993
name: GSFileHandleWriteCompletionNotification
1056
[nc removeObserver: self name: nil object: sock];
995
1057
[sock closeFile];
997
1059
connectionState = idle;
1061
NSLog(@"%@ restart on new connection", NSStringFromSelector(_cmd));
998
1062
[self _tryLoadInBackground: u];
1244
NSLog(@"%@ check for reusable socket", NSStringFromSelector(_cmd));
1246
/* An existing socket with keepalive may have been closed by the other
1247
* end. The portable way to detect it is to run the runloop once to
1248
* allow us to be sent a notification about end-of-file.
1249
* On unix systems (google told me it is not reliable on windows) we can
1250
* simply peek on the file descriptor for a much more efficient check.
1252
#if defined(__MINGW__)
1253
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
1254
NSRunLoop *loop = [NSRunLoop currentRunLoop];
1255
NSFileHandle *test = RETAIN(sock);
1257
[nc addObserver: self
1258
selector: @selector(bgdRead:)
1259
name: NSFileHandleReadCompletionNotification
1261
if ([test readInProgress] == NO)
1263
[test readInBackgroundAndNotify];
1265
[loop acceptInputForMode: NSDefaultRunLoopMode
1267
[nc removeObserver: self
1272
int fd = [sock fileDescriptor];
1280
result = recv(fd, &c, 1, MSG_PEEK | MSG_DONTWAIT);
1281
if (result == 0 || (result < 0 && errno != EAGAIN && errno != EINTR))
1295
NSLog(@"%@ socket closed by remote", NSStringFromSelector(_cmd));
1299
NSLog(@"%@ socket is still open", NSStringFromSelector(_cmd));
1175
1304
if (sock == nil)
1177
1306
keepalive = NO; // New connection
1260
1389
* Tell superclass that the load failed - let it do housekeeping.
1262
1391
[self backgroundLoadDidFailWithReason:
1263
[NSString stringWithFormat: @"Unable to connect to %@:%@ ... %s",
1264
host, port, GSLastErrorStr(errno)]];
1392
[NSString stringWithFormat: @"Unable to connect to %@:%@ ... %@",
1393
host, port, [NSError _last]]];
1271
1400
name: GSFileHandleConnectCompletionNotification
1273
1402
connectionState = connecting;
1274
if (debug) NSLog(@"%@ start connect", NSStringFromSelector(_cmd));
1405
NSLog(@"%@ start connect to %@:%@",
1406
NSStringFromSelector(_cmd), host, port);
1285
1418
name: NSFileHandleReadCompletionNotification
1288
keepalive = YES; // Reusing a connection.
1421
/* Reusing a connection. Set flag to say that it has been kept
1422
* alive and we don't know if the other end has dropped it
1423
* until we write to it and read some response.
1289
1426
method = [request objectForKey: GSHTTPPropertyMethodKey];
1290
1427
if (method == nil)