ObjFW  Diff

Differences From Artifact [6b671b812e]:

To Artifact [aea3f9620c]:


31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
46
47
48
49
50
51

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68












69
70
71
72
73
74
75
#import "OFNumber.h"
#import "OFString.h"
#import "OFTCPSocket.h"
#import "OFURL.h"

#import "OFAlreadyConnectedException.h"
#import "OFHTTPRequestFailedException.h"

#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFInvalidServerReplyException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
#import "OFUnsupportedProtocolException.h"
#import "OFUnsupportedVersionException.h"
#import "OFWriteFailedException.h"

@interface OFHTTPClientRequestHandler: OFObject
{

	OFHTTPClient *_client;
	OFHTTPRequest *_request;
	unsigned int _redirects;
	id _context;
	bool _firstLine;
	OFString *_version;
	int _status;
	OFMutableDictionary OF_GENERIC(OFString *, OFString *) *_serverHeaders;
}

- (instancetype)initWithClient: (OFHTTPClient *)client
		       request: (OFHTTPRequest *)request
		     redirects: (unsigned int)redirects
		       context: (id)context;
- (void)start;
- (void)closeAndReconnect;
@end













@interface OFHTTPClientResponse: OFHTTPResponse <OFReadyForReadingObserving>
{
	OFTCPSocket *_socket;
	bool _hasContentLength, _chunked, _keepAlive, _atEndOfStream;
	size_t _toRead;
}







>














>

















>
>
>
>
>
>
>
>
>
>
>
>







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#import "OFNumber.h"
#import "OFString.h"
#import "OFTCPSocket.h"
#import "OFURL.h"

#import "OFAlreadyConnectedException.h"
#import "OFHTTPRequestFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
#import "OFInvalidServerReplyException.h"
#import "OFNotImplementedException.h"
#import "OFNotOpenException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFTruncatedDataException.h"
#import "OFUnsupportedProtocolException.h"
#import "OFUnsupportedVersionException.h"
#import "OFWriteFailedException.h"

@interface OFHTTPClientRequestHandler: OFObject
{
@public
	OFHTTPClient *_client;
	OFHTTPRequest *_request;
	unsigned int _redirects;
	id _context;
	bool _firstLine;
	OFString *_version;
	int _status;
	OFMutableDictionary OF_GENERIC(OFString *, OFString *) *_serverHeaders;
}

- (instancetype)initWithClient: (OFHTTPClient *)client
		       request: (OFHTTPRequest *)request
		     redirects: (unsigned int)redirects
		       context: (id)context;
- (void)start;
- (void)closeAndReconnect;
@end

@interface OFHTTPClientRequestBodyStream: OFStream <OFReadyForWritingObserving>
{
	OFHTTPClientRequestHandler *_handler;
	OFTCPSocket *_socket;
	intmax_t _contentLength, _written;
	bool _closed;
}

- (instancetype)initWithHandler: (OFHTTPClientRequestHandler *)handler
			 socket: (OFTCPSocket *)sock;
@end

@interface OFHTTPClientResponse: OFHTTPResponse <OFReadyForReadingObserving>
{
	OFTCPSocket *_socket;
	bool _hasContentLength, _chunked, _keepAlive, _atEndOfStream;
	size_t _toRead;
}
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
constructRequestString(OFHTTPRequest *request)
{
	void *pool = objc_autoreleasePoolPush();
	of_http_request_method_t method = [request method];
	OFURL *URL = [request URL];
	OFString *path;
	OFString *user = [URL user], *password = [URL password];
	OFData *body = [request body];
	OFMutableString *requestString;
	OFMutableDictionary OF_GENERIC(OFString *, OFString *) *headers;
	OFEnumerator OF_GENERIC(OFString *) *keyEnumerator, *objectEnumerator;
	OFString *key, *object;

	if ([URL path] != nil)
		path = [URL URLEncodedPath];







<







97
98
99
100
101
102
103

104
105
106
107
108
109
110
constructRequestString(OFHTTPRequest *request)
{
	void *pool = objc_autoreleasePoolPush();
	of_http_request_method_t method = [request method];
	OFURL *URL = [request URL];
	OFString *path;
	OFString *user = [URL user], *password = [URL password];

	OFMutableString *requestString;
	OFMutableDictionary OF_GENERIC(OFString *, OFString *) *headers;
	OFEnumerator OF_GENERIC(OFString *) *keyEnumerator, *objectEnumerator;
	OFString *key, *object;

	if ([URL path] != nil)
		path = [URL URLEncodedPath];
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173






174
175
176
177
178
179
180
	}

	if ([headers objectForKey: @"User-Agent"] == nil)
		[headers setObject: @"Something using ObjFW "
				    @"<https://heap.zone/objfw>"
			    forKey: @"User-Agent"];

	if (body != nil) {
		if ([headers objectForKey: @"Content-Length"] == nil) {
			OFString *contentLength = [OFString stringWithFormat:
			    @"%zd", [body itemSize] * [body count]];

			[headers setObject: contentLength
				    forKey: @"Content-Length"];
		}

		if ([headers objectForKey: @"Content-Type"] == nil)
			[headers setObject: @"application/x-www-form-"
					    @"urlencoded; charset=UTF-8"
				    forKey: @"Content-Type"];
	}

	if ([request protocolVersion].major == 1 &&
	    [request protocolVersion].minor == 0 &&
	    [headers objectForKey: @"Connection"] == nil)
		[headers setObject: @"keep-alive"
			    forKey: @"Connection"];







	keyEnumerator = [headers keyEnumerator];
	objectEnumerator = [headers objectEnumerator];

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







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<





>
>
>
>
>
>







160
161
162
163
164
165
166















167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
	}

	if ([headers objectForKey: @"User-Agent"] == nil)
		[headers setObject: @"Something using ObjFW "
				    @"<https://heap.zone/objfw>"
			    forKey: @"User-Agent"];
















	if ([request protocolVersion].major == 1 &&
	    [request protocolVersion].minor == 0 &&
	    [headers objectForKey: @"Connection"] == nil)
		[headers setObject: @"keep-alive"
			    forKey: @"Connection"];

	if ([headers objectForKey: @"Content-Length"] != nil &&
	    [headers objectForKey: @"Content-Type"] == nil)
		[headers setObject: @"application/x-www-form-"
				    @"urlencoded; charset=UTF-8"
			    forKey: @"Content-Type"];

	keyEnumerator = [headers keyEnumerator];
	objectEnumerator = [headers objectEnumerator];

	while ((key = [keyEnumerator nextObject]) != nil &&
	    (object = [objectEnumerator nextObject]) != nil)
		[requestString appendFormat: @"%@: %@\r\n", key, object];
347
348
349
350
351
352
353
354

355
356
357
358
359
360
361
362
363
364
365
366
367

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

						[newHeaders
						    removeObjectForKey: key];

				[newRequest setMethod:
				    OF_HTTP_REQUEST_METHOD_GET];
				[newRequest setBody: nil];
			}

			[newRequest setURL: newURL];
			[newRequest setHeaders: newHeaders];

			_client->_inProgress = false;








|
>





<







351
352
353
354
355
356
357
358
359
360
361
362
363
364

365
366
367
368
369
370
371

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

				[newRequest setMethod:
				    OF_HTTP_REQUEST_METHOD_GET];

			}

			[newRequest setURL: newURL];
			[newRequest setHeaders: newHeaders];

			_client->_inProgress = false;

391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
- (void)createResponseWithSocket: (OFTCPSocket *)sock
{
	@try {
		[self createResponseWithSocketOrThrow: sock];
	} @catch (id e) {
		[_client->_delegate client: _client
		     didEncounterException: e
				forRequest: _request
				   context: _context];
	}
}

- (bool)handleFirstLine: (OFString *)line
{
	/*







|







395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
- (void)createResponseWithSocket: (OFTCPSocket *)sock
{
	@try {
		[self createResponseWithSocketOrThrow: sock];
	} @catch (id e) {
		[_client->_delegate client: _client
		     didEncounterException: e
				   request: _request
				   context: _context];
	}
}

- (bool)handleFirstLine: (OFString *)line
{
	/*
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
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576

577




578






579

580
581
582
583

584
585
586
587
588
589
590
591
592
593
594
595
596
597
	if (exception != nil) {
		if ([exception isKindOfClass:
		    [OFInvalidEncodingException class]])
			exception = [OFInvalidServerReplyException exception];

		[_client->_delegate client: _client
		     didEncounterException: exception
				forRequest: _request
				   context: _context];
		return false;
	}

	@try {
		if (_firstLine) {
			_firstLine = false;
			ret = [self handleFirstLine: line];
		} else
			ret = [self handleServerHeader: line
						socket: sock];
	} @catch (id e) {
		[_client->_delegate client: _client
		     didEncounterException: e
				forRequest: _request
				   context: _context];
		ret = false;
	}

	return ret;
}

- (size_t)socket: (OFTCPSocket *)sock
    didWriteBody: (const void **)body
	  length: (size_t)length
	 context: (id)context
       exception: (id)exception
{
	if (exception != nil) {
		[_client->_delegate client: _client
		     didEncounterException: exception
				forRequest: _request
				   context: _context];
		return 0;
	}

	_firstLine = true;
	[sock asyncReadLineWithTarget: self
			     selector: @selector(socket:didReadLine:context:
					   exception:)
			      context: nil];
	return 0;
}

-  (size_t)socket: (OFTCPSocket *)sock
  didWriteRequest: (const void **)request
	   length: (size_t)length
	  context: (id)context
	exception: (id)exception
{
	OFData *body;

	if (exception != nil) {
		if ([exception isKindOfClass: [OFWriteFailedException class]] &&
		    ([exception errNo] == ECONNRESET ||
		    [exception errNo] == EPIPE)) {
			/* In case a keep-alive connection timed out */
			[self closeAndReconnect];
			return 0;
		}

		[_client->_delegate client: _client
		     didEncounterException: exception
				forRequest: _request
				   context: _context];
		return 0;
	}


	if ((body = [_request body]) != nil) {




		[sock asyncWriteBuffer: [body items]






				length: [body count] * [body itemSize]

				target: self
			      selector: @selector(socket:didWriteBody:length:
					    context:exception:)
			       context: nil];

		return 0;
	} else
		return [self socket: sock
		       didWriteBody: NULL
			     length: 0
			    context: nil
			  exception: nil];
}

- (void)handleSocket: (OFTCPSocket *)sock
{
	/*
	 * 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







|














|







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






<
<











|




>
|
>
>
>
>
|
>
>
>
>
>
>
|
>
|
|
|
|
>
|
<
<
<
<
<
<







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
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577






578
579
580
581
582
583
584
	if (exception != nil) {
		if ([exception isKindOfClass:
		    [OFInvalidEncodingException class]])
			exception = [OFInvalidServerReplyException exception];

		[_client->_delegate client: _client
		     didEncounterException: exception
				   request: _request
				   context: _context];
		return false;
	}

	@try {
		if (_firstLine) {
			_firstLine = false;
			ret = [self handleFirstLine: line];
		} else
			ret = [self handleServerHeader: line
						socket: sock];
	} @catch (id e) {
		[_client->_delegate client: _client
		     didEncounterException: e
				   request: _request
				   context: _context];
		ret = false;
	}

	return ret;
}























-  (size_t)socket: (OFTCPSocket *)sock
  didWriteRequest: (const void **)request
	   length: (size_t)length
	  context: (id)context
	exception: (id)exception
{


	if (exception != nil) {
		if ([exception isKindOfClass: [OFWriteFailedException class]] &&
		    ([exception errNo] == ECONNRESET ||
		    [exception errNo] == EPIPE)) {
			/* In case a keep-alive connection timed out */
			[self closeAndReconnect];
			return 0;
		}

		[_client->_delegate client: _client
		     didEncounterException: exception
				   request: _request
				   context: _context];
		return 0;
	}

	_firstLine = true;

	if ([[_request headers] objectForKey: @"Content-Length"] != nil) {
		OFStream *stream = [[[OFHTTPClientRequestBodyStream alloc]
		    initWithHandler: self
			     socket: sock] autorelease];

		if ([_client->_delegate respondsToSelector:
		    @selector(client:requestsBody:request:context:)])
			[_client->_delegate client: _client
				      requestsBody: stream
					   request: _request
					   context: _context];

	} else
		[sock asyncReadLineWithTarget: self
				     selector: @selector(socket:didReadLine:
						   context:exception:)
				      context: nil];

	return 0;






}

- (void)handleSocket: (OFTCPSocket *)sock
{
	/*
	 * 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
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
				target: self
			      selector: @selector(socket:didWriteRequest:
					    length:context:exception:)
			       context: requestString];
	} @catch (id e) {
		[_client->_delegate client: _client
		     didEncounterException: e
				forRequest: _request
				   context: _context];
		return;
	}
}

- (void)socketDidConnect: (OFTCPSocket *)sock
		 context: (id)context
	       exception: (id)exception
{
	if (exception != nil) {
		[_client->_delegate client: _client
		     didEncounterException: exception
				forRequest: _request
				   context: _context];
		return;
	}

	if ([_client->_delegate respondsToSelector:
	    @selector(client:didCreateSocket:forRequest:context:)])
		[_client->_delegate client: _client
			   didCreateSocket: sock
				forRequest: _request
				   context: _context];

	[self performSelector: @selector(handleSocket:)
		   withObject: sock
		   afterDelay: 0];
}

- (bool)throwAwayContent: (OFHTTPClientResponse *)response
		  buffer: (char *)buffer
		  length: (size_t)length
		 context: (OFTCPSocket *)sock
	       exception: (id)exception
{
	if (exception != nil) {
		[_client->_delegate client: _client
		     didEncounterException: exception
				forRequest: _request
				   context: _context];
		return false;
	}

	if ([response isAtEndOfStream]) {
		[self freeMemory: buffer];








|












|





|


|
















|







602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
				target: self
			      selector: @selector(socket:didWriteRequest:
					    length:context:exception:)
			       context: requestString];
	} @catch (id e) {
		[_client->_delegate client: _client
		     didEncounterException: e
				   request: _request
				   context: _context];
		return;
	}
}

- (void)socketDidConnect: (OFTCPSocket *)sock
		 context: (id)context
	       exception: (id)exception
{
	if (exception != nil) {
		[_client->_delegate client: _client
		     didEncounterException: exception
				   request: _request
				   context: _context];
		return;
	}

	if ([_client->_delegate respondsToSelector:
	    @selector(client:didCreateSocket:request:context:)])
		[_client->_delegate client: _client
			   didCreateSocket: sock
				   request: _request
				   context: _context];

	[self performSelector: @selector(handleSocket:)
		   withObject: sock
		   afterDelay: 0];
}

- (bool)throwAwayContent: (OFHTTPClientResponse *)response
		  buffer: (char *)buffer
		  length: (size_t)length
		 context: (OFTCPSocket *)sock
	       exception: (id)exception
{
	if (exception != nil) {
		[_client->_delegate client: _client
		     didEncounterException: exception
				   request: _request
				   context: _context];
		return false;
	}

	if ([response isAtEndOfStream]) {
		[self freeMemory: buffer];

754
755
756
757
758
759
760
761
762
763
764
765





























































































766
767
768
769
770
771
772
				  target: self
				selector: @selector(socketDidConnect:context:
					      exception:)
				 context: nil];
	} @catch (id e) {
		[_client->_delegate client: _client
		     didEncounterException: e
				forRequest: _request
				   context: _context];
	}
}
@end






























































































@implementation OFHTTPClientResponse
@synthesize of_keepAlive = _keepAlive;

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







|




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
				  target: self
				selector: @selector(socketDidConnect:context:
					      exception:)
				 context: nil];
	} @catch (id e) {
		[_client->_delegate client: _client
		     didEncounterException: e
				   request: _request
				   context: _context];
	}
}
@end

@implementation OFHTTPClientRequestBodyStream
- (instancetype)initWithHandler: (OFHTTPClientRequestHandler *)handler
			 socket: (OFTCPSocket *)sock
{
	self = [super init];

	@try {
		OFDictionary OF_GENERIC(OFString *, OFString *) *headers;
		OFString *contentLengthString;

		_handler = [handler retain];
		_socket = [sock retain];

		headers = [_handler->_request headers];

		contentLengthString = [headers objectForKey: @"Content-Length"];
		if (contentLengthString == nil)
			@throw [OFInvalidArgumentException exception];

		_contentLength = [contentLengthString decimalValue];
		if (_contentLength < 0)
			@throw [OFOutOfRangeException exception];

		if ([headers objectForKey: @"Transfer-Encoding"] != nil)
			@throw [OFInvalidArgumentException exception];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[self close];

	[_handler release];
	[_socket release];

	[super dealloc];
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer
		       length: (size_t)length
{
	size_t written;

#if SIZE_MAX >= INTMAX_MAX
	if (length > INTMAX_MAX)
		@throw [OFOutOfRangeException exception];
#endif

	if (INTMAX_MAX - _written < (intmax_t)length ||
	    _written + (intmax_t)length > _contentLength)
		@throw [OFOutOfRangeException exception];

	written = [_socket writeBuffer: buffer
				length: length];

#if SIZE_MAX >= INTMAX_MAX
	if (written > INTMAX_MAX)
		@throw [OFOutOfRangeException exception];
#endif

	if (INTMAX_MAX - _written < (intmax_t)written)
		@throw [OFOutOfRangeException exception];

	_written += written;

	return written;
}

- (void)close
{
	if (_closed)
		return;

	if (_written < _contentLength)
		@throw [OFTruncatedDataException exception];

	[_socket asyncReadLineWithTarget: _handler
				selector: @selector(socket:didReadLine:context:
					      exception:)
				 context: nil];
}

- (int)fileDescriptorForWriting
{
	return [_socket fileDescriptorForWriting];
}
@end

@implementation OFHTTPClientResponse
@synthesize of_keepAlive = _keepAlive;

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