ObjFW  Diff

Differences From Artifact [29b75ba101]:

To Artifact [0843f75aca]:


68
69
70
71
72
73
74
75

76
77
78
79
80
81

82
83
84
85

86
87
88
89
90
91
92
68
69
70
71
72
73
74

75
76
77
78
79
80

81
82
83
84

85
86
87
88
89
90
91
92







-
+





-
+



-
+







	OFTCPSocket *_socket;
	bool _hasContentLength, _chunked, _keepAlive, _atEndOfStream;
	size_t _toRead;
}

@property (nonatomic, setter=of_setKeepAlive:) bool of_keepAlive;

- initWithSocket: (OFTCPSocket *)socket;
- initWithSocket: (OFTCPSocket *)sock;
@end

@implementation OFHTTPClientResponse
@synthesize of_keepAlive = _keepAlive;

- initWithSocket: (OFTCPSocket *)socket
- initWithSocket: (OFTCPSocket *)sock
{
	self = [super init];

	_socket = [socket retain];
	_socket = [sock retain];

	return self;
}

- (void)dealloc
{
	[_socket release];
284
285
286
287
288
289
290
291

292
293
294
295
296
297
298
299
300

301
302
303

304
305
306
307
308

309
310
311
312


313
314

315
316
317
318
319
320
321
322
323
324
325
326
327
328
329

330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350

351
352
353
354
355
356
357
284
285
286
287
288
289
290

291
292
293
294
295
296
297
298
299

300

301

302
303
304
305
306

307
308
309


310
311
312

313
314
315
316
317
318
319
320
321
322
323
324
325
326
327

328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348

349
350
351
352
353
354
355
356







-
+








-
+
-

-
+




-
+


-
-
+
+

-
+














-
+




















-
+







	return [self performRequest: request
			  redirects: 10];
}

- (OFTCPSocket *)of_closeAndCreateSocketForRequest: (OFHTTPRequest *)request
{
	OFURL *URL = [request URL];
	OFTCPSocket *socket;
	OFTCPSocket *sock;

	[self close];

	if ([[URL scheme] isEqual: @"https"]) {
		if (of_tls_socket_class == Nil)
			@throw [OFUnsupportedProtocolException
			    exceptionWithURL: URL];

		socket = [[[of_tls_socket_class alloc] init]
		sock = [[[of_tls_socket_class alloc] init] autorelease];
		    autorelease];
	} else
		socket = [OFTCPSocket socket];
		sock = [OFTCPSocket socket];

	if ([_delegate respondsToSelector:
	    @selector(client:didCreateSocket:request:)])
		[_delegate client: self
		  didCreateSocket: socket
		  didCreateSocket: sock
			  request: request];

	[socket connectToHost: [URL host]
			 port: [URL port]];
	[sock connectToHost: [URL host]
		       port: [URL port]];

	return socket;
	return sock;
}

- (OFHTTPResponse *)performRequest: (OFHTTPRequest *)request
			 redirects: (size_t)redirects
{
	void *pool = objc_autoreleasePoolPush();
	OFURL *URL = [request URL];
	OFString *scheme = [URL scheme];
	of_http_request_method_t method = [request method];
	OFString *path;
	OFMutableString *requestString;
	OFString *user, *password;
	OFMutableDictionary OF_GENERIC(OFString *, OFString *) *headers;
	OFData *body = [request body];
	OFTCPSocket *socket;
	OFTCPSocket *sock;
	OFHTTPClientResponse *response;
	OFString *line, *version, *redirect, *connectionHeader;
	bool keepAlive;
	OFMutableDictionary OF_GENERIC(OFString *, OFString *) *serverHeaders;
	OFEnumerator *keyEnumerator, *objectEnumerator;
	OFString *key, *object;
	int status;

	if (![scheme isEqual: @"http"] && ![scheme isEqual: @"https"])
		@throw [OFUnsupportedProtocolException exceptionWithURL: URL];

	/* Can we reuse the socket? */
	if (_socket != nil && [[_lastURL scheme] isEqual: scheme] &&
	    [[_lastURL host] isEqual: [URL host]] &&
	    [_lastURL port] == [URL port]) {
		/*
		 * Set _socket to nil, so that in case of an error it won't be
		 * reused. If everything is successful, we set _socket again
		 * at the end.
		 */
		socket = [_socket autorelease];
		sock = [_socket autorelease];
		_socket = nil;

		[_lastURL release];
		_lastURL = nil;

		@try {
			if (!_lastWasHEAD) {
367
368
369
370
371
372
373
374

375
376
377
378
379
380
381
366
367
368
369
370
371
372

373
374
375
376
377
378
379
380







-
+







				}
			}
		} @finally {
			[_lastResponse release];
			_lastResponse = nil;
		}
	} else
		socket = [self of_closeAndCreateSocketForRequest: request];
		sock = [self of_closeAndCreateSocketForRequest: request];

	/*
	 * As a work around for a bug with split packets in lighttpd when using
	 * HTTPS, we construct the complete request in a buffer string and then
	 * send it all at once.
	 *
	 * We do not use the socket's write buffer in case we need to resend
468
469
470
471
472
473
474
475

476
477
478
479
480
481
482


483
484
485
486
487


488
489
490

491
492
493
494
495
496
497
498
499
500
501
502


503
504
505
506


507
508
509
510

511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530

531
532
533
534
535

536
537
538
539
540
541
542
467
468
469
470
471
472
473

474
475
476
477
478
479


480
481
482
483
484


485
486
487
488

489
490
491
492
493
494
495
496
497
498
499


500
501
502
503


504
505

506
507

508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527

528
529
530
531
532

533
534
535
536
537
538
539
540







-
+





-
-
+
+



-
-
+
+


-
+










-
-
+
+


-
-
+
+
-


-
+



















-
+




-
+







	while ((key = [keyEnumerator nextObject]) != nil &&
	    (object = [objectEnumerator nextObject]) != nil)
		[requestString appendFormat: @"%@: %@\r\n", key, object];

	[requestString appendString: @"\r\n"];

	@try {
		[socket writeString: requestString];
		[sock writeString: requestString];
	} @catch (OFWriteFailedException *e) {
		if ([e errNo] != ECONNRESET && [e errNo] != EPIPE)
			@throw e;

		/* Reconnect in case a keep-alive connection timed out */
		socket = [self of_closeAndCreateSocketForRequest: request];
		[socket writeString: requestString];
		sock = [self of_closeAndCreateSocketForRequest: request];
		[sock writeString: requestString];
	}

	if (body != nil)
		[socket writeBuffer: [body items]
			     length: [body count] * [body itemSize]];
		[sock writeBuffer: [body items]
			   length: [body count] * [body itemSize]];

	@try {
		line = [socket readLine];
		line = [sock readLine];
	} @catch (OFInvalidEncodingException *e) {
		@throw [OFInvalidServerReplyException exception];
	}

	/*
	 * It's possible that the write succeeds on a connection that is
	 * keep-alive, but the connection has already been closed by the remote
	 * end due to a timeout. In this case, we need to reconnect.
	 */
	if (line == nil) {
		socket = [self of_closeAndCreateSocketForRequest: request];
		[socket writeString: requestString];
		sock = [self of_closeAndCreateSocketForRequest: request];
		[sock writeString: requestString];

		if (body != nil)
			[socket writeBuffer: [body items]
				     length: [body count] *
			[sock writeBuffer: [body items]
				   length: [body count] * [body itemSize]];
					     [body itemSize]];

		@try {
			line = [socket readLine];
			line = [sock readLine];
		} @catch (OFInvalidEncodingException *e) {
			@throw [OFInvalidServerReplyException exception];
		}
	}

	if (![line hasPrefix: @"HTTP/"] || [line length] < 9 ||
	    [line characterAtIndex: 8] != ' ')
		@throw [OFInvalidServerReplyException exception];

	version = [line substringWithRange: of_range(5, 3)];
	if (![version isEqual: @"1.0"] && ![version isEqual: @"1.1"])
		@throw [OFUnsupportedVersionException
		    exceptionWithVersion: version];

	status = (int)[[line substringWithRange: of_range(9, 3)] decimalValue];

	serverHeaders = [OFMutableDictionary dictionary];

	for (;;) {
		OFString *key, *value, *old;
		OFString *value, *old;
		const char *lineC, *tmp;
		char *keyC;

		@try {
			line = [socket readLine];
			line = [sock readLine];
		} @catch (OFInvalidEncodingException *e) {
			@throw [OFInvalidServerReplyException exception];
		}

		if (line == nil)
			@throw [OFInvalidServerReplyException exception];

583
584
585
586
587
588
589
590

591
592
593
594
595
596
597
581
582
583
584
585
586
587

588
589
590
591
592
593
594
595







-
+







	if ([_delegate respondsToSelector:
	    @selector(client:didReceiveHeaders:statusCode:request:)])
		[_delegate     client: self
		    didReceiveHeaders: serverHeaders
			   statusCode: status
			      request: request];

	response = [[[OFHTTPClientResponse alloc] initWithSocket: socket]
	response = [[[OFHTTPClientResponse alloc] initWithSocket: sock]
	    autorelease];
	[response setProtocolVersionFromString: version];
	[response setStatusCode: status];
	[response setHeaders: serverHeaders];

	connectionHeader = [serverHeaders objectForKey: @"Connection"];
	if ([version isEqual: @"1.1"]) {
607
608
609
610
611
612
613
614

615
616
617
618
619
620
621
605
606
607
608
609
610
611

612
613
614
615
616
617
618
619







-
+







		else
			keepAlive = false;
	}

	if (keepAlive) {
		[response of_setKeepAlive: true];

		_socket = [socket retain];
		_socket = [sock retain];
		_lastURL = [URL copy];
		_lastWasHEAD = (method == OF_HTTP_REQUEST_METHOD_HEAD);
		_lastResponse = [response retain];
	}

	/* FIXME: Case-insensitive check of redirect's scheme */
	if (redirects > 0 && (status == 301 || status == 302 ||
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
665
666
667
668
669
670
671



672
673
674
675
676
677
678







-
-
-








			/*
			 * 303 means the request should be converted to a GET
			 * request before redirection. This also means stripping
			 * the entity of the request.
			 */
			if (status == 303) {
				OFEnumerator *keyEnumerator, *objectEnumerator;
				id key, object;

				keyEnumerator = [headers keyEnumerator];
				objectEnumerator = [headers objectEnumerator];
				while ((key = [keyEnumerator nextObject]) !=
				    nil &&
				    (object = [objectEnumerator nextObject]) !=
				    nil)
					if ([key hasPrefix: @"Content-"])