~ubuntu-branches/ubuntu/precise/gnustep-base/precise

« back to all changes in this revision

Viewing changes to Source/GSHTTPURLHandle.m

  • Committer: Bazaar Package Importer
  • Author(s): Hubert Chathi
  • Date: 2007-10-03 17:16:39 UTC
  • mfrom: (1.2.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20071003171639-rtyga33jz04drwe3
Tags: 1.14.0-2
Upload to unstable.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
 
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"
48
49
 
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
57
58
#endif
 
59
#ifdef  HAVE_SYS_SOCKET_H
 
60
#include <sys/socket.h>         // For MSG_PEEK, etc
 
61
#endif
58
62
 
59
63
/*
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 *);
83
87
 
84
 
const NSMapTableKeyCallBacks writeKeyCallBacks =
 
88
static const NSMapTableKeyCallBacks writeKeyCallBacks =
85
89
{
86
90
  (NSMT_hash_func_t) _non_retained_id_hash,
87
91
  (NSMT_is_equal_func_t) _non_retained_id_is_equal,
210
214
 */
211
215
@implementation GSHTTPURLHandle
212
216
 
 
217
#define MAX_CACHED      16
 
218
 
213
219
static NSMutableDictionary      *urlCache = nil;
 
220
static NSMutableArray           *urlOrder = nil;
214
221
static NSLock                   *urlLock = nil;
215
222
 
216
223
static Class                    sslClass = 0;
278
285
      //NSLog(@"Lookup for handle for '%@'", page);
279
286
      [urlLock lock];
280
287
      obj = [urlCache objectForKey: page];
281
 
      AUTORELEASE(RETAIN(obj));
 
288
      if (obj != nil)
 
289
        {
 
290
          [urlOrder removeObjectIdenticalTo: obj];
 
291
          [urlOrder addObject: obj];
 
292
          AUTORELEASE(RETAIN(obj));
 
293
        }
282
294
      [urlLock unlock];
283
295
      //NSLog(@"Found handle %@", obj);
284
296
    }
290
302
  if (self == [GSHTTPURLHandle class])
291
303
    {
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",
309
322
    {
310
323
      NSNotificationCenter      *nc = [NSNotificationCenter defaultCenter];
311
324
 
312
 
      /*
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.
316
 
       */
317
 
      [nc removeObserver: self
318
 
                    name: NSFileHandleReadCompletionNotification
319
 
                  object: sock];
 
325
      [nc removeObserver: self name: nil object: sock];
320
326
      [sock closeFile];
321
327
      DESTROY(sock);
322
328
    }
350
356
      connectionState = idle;
351
357
      if (cached == YES)
352
358
        {
353
 
          NSString      *page = [newUrl absoluteString];
 
359
          NSString              *page = [newUrl absoluteString];
 
360
          GSHTTPURLHandle       *obj;
354
361
 
355
362
          [urlLock lock];
 
363
          obj = [urlCache objectForKey: page];
356
364
          [urlCache setObject: self forKey: page];
 
365
          if (obj != nil)
 
366
            {
 
367
              [urlOrder removeObjectIdenticalTo: obj];
 
368
            }
 
369
          [urlOrder addObject: self];
 
370
          while ([urlOrder count] > MAX_CACHED)
 
371
            {
 
372
              obj = [urlOrder objectAtIndex: 0];
 
373
              [urlCache removeObjectForKey: [obj->url absoluteString]];
 
374
              [urlOrder removeObjectAtIndex: 0];
 
375
            }
357
376
          [urlLock unlock];
358
377
          //NSLog(@"Cache handle %@ for '%@'", self, page);
359
378
        }
433
452
          NSURLCredential       *cred;
434
453
          NSString              *method;
435
454
 
436
 
          /*
437
 
           * Create credential from user and password
438
 
           * stored in the URL.
 
455
          /* Create credential from user and password stored in the URL.
 
456
           * Returns nil if we have no username or password.
439
457
           */
440
458
          cred = [[NSURLCredential alloc]
441
459
            initWithUser: [u user]
442
460
            password: [u password]
443
461
            persistence: NSURLCredentialPersistenceForSession];
444
462
 
445
 
          authentication = [GSHTTPAuthentication
446
 
            authenticationWithCredential: cred
447
 
            inProtectionSpace: space];
448
 
 
449
 
          RELEASE(cred);
 
463
          if (cred == nil)
 
464
            {
 
465
              authentication = nil;
 
466
            }
 
467
          else
 
468
            {
 
469
              /* Create authentication from credential ... returns nil if
 
470
               * we have no credential.
 
471
               */
 
472
              authentication = [GSHTTPAuthentication
 
473
                authenticationWithCredential: cred
 
474
                inProtectionSpace: space];
 
475
              RELEASE(cred);
 
476
            }
450
477
 
451
478
          method = [request objectForKey: GSHTTPPropertyMethodKey];
452
479
          if (method == nil)
464
491
          auth = [authentication authorizationForAuthentication: nil
465
492
                                                         method: method
466
493
                                                           path: [u path]];
467
 
 
468
 
          NSMapInsert(wProperties, (void*)@"Authorization", (void*)auth);
 
494
          /* If authentication is nil then auth will also be nil
 
495
           */
 
496
          if (auth != nil)
 
497
            {
 
498
              [self writeProperty: auth forKey: @"Authorization"];
 
499
            }
469
500
        }
470
501
    }
471
502
 
512
543
  NSDictionary          *dict = [not userInfo];
513
544
  NSData                *d;
514
545
  NSRange               r;
 
546
  unsigned              readCount;
515
547
  BOOL                  complete = NO;
516
548
 
517
549
  RETAIN(self);
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];
522
555
 
523
556
  if (connectionState == idle)
524
557
    {
529
562
       */
530
563
      if (debug == YES && [d length] != 0)
531
564
        {
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]);
534
568
        }
535
 
      [nc removeObserver: self
536
 
                    name: NSFileHandleReadCompletionNotification
537
 
                  object: sock];
 
569
      [nc removeObserver: self name: nil object: sock];
538
570
      [sock closeFile];
539
571
      DESTROY(sock);
540
572
    }
585
617
          GSMimeHeader  *info;
586
618
          NSString      *val;
587
619
          float         ver;
 
620
          int           code;
588
621
 
589
622
          connectionState = idle;
 
623
          [nc removeObserver: self name: nil object: sock];
590
624
 
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))
594
628
            {
595
 
              [nc removeObserver: self
596
 
                            name: NSFileHandleReadCompletionNotification
597
 
                          object: sock];
 
629
              [nc removeObserver: self name: nil object: sock];
598
630
              [sock closeFile];
599
631
              DESTROY(sock);
600
632
            }
604
636
           */
605
637
          info = [document headerNamed: @"http"];
606
638
          val = [info objectForKey: NSHTTPPropertyStatusCodeKey];
607
 
          if ([val intValue] == 401 && self->challenged < 2)
 
639
          code = [val intValue];
 
640
          if (code == 401 && self->challenged < 2)
608
641
            {
609
642
              GSMimeHeader      *ah;
610
643
 
613
646
                {
614
647
                  NSURLProtectionSpace  *space;
615
648
                  NSString              *ac;
616
 
                  NSURLCredential       *cred;
617
649
                  GSHTTPAuthentication  *authentication;
618
650
                  NSString              *method;
619
 
                  NSString              *a;
 
651
                  NSString              *auth;
620
652
 
621
653
                  ac = [ah value];
622
654
                  space = [GSHTTPAuthentication
623
655
                    protectionSpaceForAuthentication: ac requestURL: url];
624
 
 
625
 
                  /*
626
 
                   * Create credential from user and password
627
 
                   * stored in the URL.
628
 
                   */
629
 
                  cred = [[NSURLCredential alloc]
630
 
                    initWithUser: [url user]
631
 
                    password: [url password]
632
 
                    persistence: NSURLCredentialPersistenceForSession];
633
 
 
634
 
                  /*
635
 
                   * Get the digest object and ask it for a header
636
 
                   * to use for authorisation.
637
 
                   */
638
 
                  authentication = [GSHTTPAuthentication
639
 
                    authenticationWithCredential: cred
640
 
                    inProtectionSpace: space];
641
 
 
642
 
                  RELEASE(cred);
 
656
                  if (space == nil)
 
657
                    {
 
658
                      authentication = nil;
 
659
                    }
 
660
                  else
 
661
                    {
 
662
                      NSURLCredential   *cred;
 
663
 
 
664
                      /*
 
665
                       * Create credential from user and password
 
666
                       * stored in the URL.
 
667
                       * Returns nil if we have no username or password.
 
668
                       */
 
669
                      cred = [[NSURLCredential alloc]
 
670
                        initWithUser: [url user]
 
671
                        password: [url password]
 
672
                        persistence: NSURLCredentialPersistenceForSession];
 
673
 
 
674
                      if (cred == nil)
 
675
                        {
 
676
                          authentication = nil;
 
677
                        }
 
678
                      else
 
679
                        {
 
680
                          /*
 
681
                           * Get the digest object and ask it for a header
 
682
                           * to use for authorisation.
 
683
                           * Returns nil if we have no credential.
 
684
                           */
 
685
                          authentication = [GSHTTPAuthentication
 
686
                            authenticationWithCredential: cred
 
687
                            inProtectionSpace: space];
 
688
                          RELEASE(cred);
 
689
                        }
 
690
                    }
643
691
 
644
692
                  method = [request objectForKey: GSHTTPPropertyMethodKey];
645
693
                  if (method == nil)
654
702
                        }
655
703
                    }
656
704
 
657
 
                  a = [authentication authorizationForAuthentication: ac
658
 
                                                          method: method
659
 
                                                            path: [url path]];
660
 
                  if (a != nil)
 
705
                  auth = [authentication authorizationForAuthentication: ac
 
706
                    method: method
 
707
                    path: [url path]];
 
708
                  if (auth != nil)
661
709
                    {
662
 
                      [self writeProperty: a forKey: @"Authorization"];
 
710
                      [self writeProperty: auth forKey: @"Authorization"];
663
711
                      [self _tryLoadInBackground: u];
664
712
                      return;   // Retrying.
665
713
                    }
688
736
          bodyPos = 0;
689
737
          DESTROY(wData);
690
738
          NSResetMapTable(wProperties);
691
 
          [self didLoadBytes: [d subdataWithRange: r]
692
 
                loadComplete: YES];
 
739
          if (code >= 200 && code < 300)
 
740
            {
 
741
              [self didLoadBytes: [d subdataWithRange: r]
 
742
                    loadComplete: YES];
 
743
            }
 
744
          else
 
745
            {
 
746
              [self didLoadBytes: [d subdataWithRange: r]
 
747
                    loadComplete: NO];
 
748
              [self cancelLoadInBackground];
 
749
            }
693
750
        }
694
751
      else
695
752
        {
705
762
                    loadComplete: NO];
706
763
            }
707
764
        }
708
 
      if (sock != nil
709
 
        && (connectionState == reading || connectionState == idle))
 
765
 
 
766
      if (complete == NO && readCount == 0)
 
767
        {
 
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.
 
772
           */
 
773
          if (debug == YES)
 
774
            {
 
775
              NSLog(@"HTTP response not received - %@", parser);
 
776
            }
 
777
          [self endLoadInBackground];
 
778
          [self backgroundLoadDidFailWithReason: @"Response parse failed"];
 
779
        }
 
780
 
 
781
      if (sock != nil && connectionState == reading)
710
782
        {
711
783
          if ([sock readInProgress] == NO)
712
784
            {
780
852
  if (connectionState != idle)
781
853
    {
782
854
      NSNotificationCenter      *nc = [NSNotificationCenter defaultCenter];
783
 
      NSString                  *name;
784
 
 
785
 
      if (connectionState == connecting)
786
 
        name = GSFileHandleConnectCompletionNotification;
787
 
      else if (connectionState == writing)
788
 
        name = GSFileHandleWriteCompletionNotification;
789
 
      else
790
 
        name = NSFileHandleReadCompletionNotification;
791
 
 
792
 
      [nc removeObserver: self name: name object: sock];
 
855
 
 
856
      [nc removeObserver: self name: nil object: sock];
793
857
      [sock closeFile];
794
858
      DESTROY(sock);
795
859
      connectionState = idle;
989
1053
           * then we may try again with a new connection.
990
1054
           */
991
1055
          nc = [NSNotificationCenter defaultCenter];
992
 
          [nc removeObserver: self
993
 
                        name: GSFileHandleWriteCompletionNotification
994
 
                      object: sock];
 
1056
          [nc removeObserver: self name: nil object: sock];
995
1057
          [sock closeFile];
996
1058
          DESTROY(sock);
997
1059
          connectionState = idle;
 
1060
          if (debug)
 
1061
            NSLog(@"%@ restart on new connection", NSStringFromSelector(_cmd));
998
1062
          [self _tryLoadInBackground: u];
999
1063
          return;
1000
1064
        }
1134
1198
  [dat setLength: 0];
1135
1199
  RELEASE(document);
1136
1200
  RELEASE(parser);
 
1201
  [pageInfo removeAllObjects];
1137
1202
  parser = [GSMimeParser new];
1138
1203
  document = RETAIN([parser mimeDocument]);
1139
1204
 
1172
1237
      port = @"80";
1173
1238
    }
1174
1239
 
 
1240
  if (sock != nil)
 
1241
    {
 
1242
      if (debug)
 
1243
        {
 
1244
          NSLog(@"%@ check for reusable socket", NSStringFromSelector(_cmd));
 
1245
        }
 
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.
 
1251
       */
 
1252
#if     defined(__MINGW__)
 
1253
      NSNotificationCenter      *nc = [NSNotificationCenter defaultCenter];
 
1254
      NSRunLoop                 *loop = [NSRunLoop currentRunLoop];
 
1255
      NSFileHandle              *test = RETAIN(sock);
 
1256
      
 
1257
      [nc addObserver: self
 
1258
             selector: @selector(bgdRead:)
 
1259
                 name: NSFileHandleReadCompletionNotification
 
1260
               object: test];
 
1261
      if ([test readInProgress] == NO)
 
1262
        {
 
1263
          [test readInBackgroundAndNotify];
 
1264
        }
 
1265
      [loop acceptInputForMode: NSDefaultRunLoopMode
 
1266
                    beforeDate: nil];
 
1267
      [nc removeObserver: self
 
1268
                    name: nil
 
1269
                  object: test];
 
1270
      RELEASE(test);
 
1271
#else
 
1272
      int       fd = [sock fileDescriptor];
 
1273
 
 
1274
      if (fd >= 0)
 
1275
        {
 
1276
          extern int    errno;
 
1277
          int           result;
 
1278
          unsigned char c;
 
1279
 
 
1280
          result = recv(fd, &c, 1, MSG_PEEK | MSG_DONTWAIT);
 
1281
          if (result == 0 || (result < 0 && errno != EAGAIN && errno != EINTR))
 
1282
            {
 
1283
              DESTROY(sock);
 
1284
            }
 
1285
        }
 
1286
      else
 
1287
        {
 
1288
          DESTROY(sock);
 
1289
        }
 
1290
#endif
 
1291
      if (debug)
 
1292
        {
 
1293
          if (sock == nil)
 
1294
            {
 
1295
              NSLog(@"%@ socket closed by remote", NSStringFromSelector(_cmd));
 
1296
            }
 
1297
          else
 
1298
            {
 
1299
              NSLog(@"%@ socket is still open", NSStringFromSelector(_cmd));
 
1300
            }
 
1301
        }
 
1302
    }
 
1303
 
1175
1304
  if (sock == nil)
1176
1305
    {
1177
1306
      keepalive = NO;   // New connection
1260
1389
           * Tell superclass that the load failed - let it do housekeeping.
1261
1390
           */
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]]];
1265
1394
          return;
1266
1395
        }
1267
1396
      RETAIN(sock);
1271
1400
                 name: GSFileHandleConnectCompletionNotification
1272
1401
               object: sock];
1273
1402
      connectionState = connecting;
1274
 
      if (debug) NSLog(@"%@ start connect", NSStringFromSelector(_cmd));
 
1403
      if (debug)
 
1404
        {
 
1405
          NSLog(@"%@ start connect to %@:%@",
 
1406
            NSStringFromSelector(_cmd), host, port);
 
1407
        }
1275
1408
    }
1276
1409
  else
1277
1410
    {
1285
1418
                    name: NSFileHandleReadCompletionNotification
1286
1419
                  object: sock];
1287
1420
 
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.
 
1424
       */
 
1425
      keepalive = YES;
1289
1426
      method = [request objectForKey: GSHTTPPropertyMethodKey];
1290
1427
      if (method == nil)
1291
1428
        {