ObjFW  Check-in [d16ad96cbd]

Overview
Comment:OFStream: Use a delegate for async operations

The target / selector approach had several drawbacks:

* It was inconvenient to use, as for every read or write, a target,
selector and context would need to be specified.
* It lacked any kind of type-safety and would not even warn about using
a callback method with the wrong number of parameters.
* It encouraged using a different callback method for each read or
write call, which results in code that is hard to follow and also
slower (as it needs to recreate the async operation with a new
callback every time).

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: d16ad96cbdf74ff1725ee86df306b24dc2fde9c3b4a1c0776d162a1cbf0a2f54
User & Date: js on 2018-12-07 01:33:47
Other Links: manifest | tags
Context
2018-12-08
16:05
OFTCPSocket: Use a delegate for async operations check-in: 27153bf8cf user: js tags: trunk
2018-12-07
01:33
OFStream: Use a delegate for async operations check-in: d16ad96cbd user: js tags: trunk
2018-12-02
20:11
OFSystemInfo: Work around GCC picking ebx for "=r" check-in: 2fc1016631 user: js tags: trunk
Changes

Modified src/OFHTTPClient.m from [1e684aefeb] to [376bac39d6].

47
48
49
50
51
52
53
54
55
56
57
58

59
60
61
62
63
64
65
#import "OFTruncatedDataException.h"
#import "OFUnsupportedProtocolException.h"
#import "OFUnsupportedVersionException.h"
#import "OFWriteFailedException.h"

#define REDIRECTS_DEFAULT 10

@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;
}







|




>







47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#import "OFTruncatedDataException.h"
#import "OFUnsupportedProtocolException.h"
#import "OFUnsupportedVersionException.h"
#import "OFWriteFailedException.h"

#define REDIRECTS_DEFAULT 10

@interface OFHTTPClientRequestHandler: OFObject <OFStreamDelegate>
{
@public
	OFHTTPClient *_client;
	OFHTTPRequest *_request;
	OFString *_requestString;
	unsigned int _redirects;
	id _context;
	bool _firstLine;
	OFString *_version;
	int _status;
	OFMutableDictionary OF_GENERIC(OFString *, OFString *) *_serverHeaders;
}
274
275
276
277
278
279
280

281
282
283
284
285
286
287
	return self;
}

- (void)dealloc
{
	[_client release];
	[_request release];

	[_context release];
	[_version release];
	[_serverHeaders release];

	[super dealloc];
}








>







275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
	return self;
}

- (void)dealloc
{
	[_client release];
	[_request release];
	[_requestString release];
	[_context release];
	[_version release];
	[_serverHeaders release];

	[super dealloc];
}

474
475
476
477
478
479
480


481
482
483
484
485
486
487
		if ([_client->_delegate respondsToSelector: @selector(client:
		    didReceiveHeaders:statusCode:request:context:)])
			[_client->_delegate client: _client
				 didReceiveHeaders: _serverHeaders
					statusCode: _status
					   request: _request
					   context: _context];



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

		return false;
	}







>
>







476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
		if ([_client->_delegate respondsToSelector: @selector(client:
		    didReceiveHeaders:statusCode:request:context:)])
			[_client->_delegate client: _client
				 didReceiveHeaders: _serverHeaders
					statusCode: _status
					   request: _request
					   context: _context];

		[sock setDelegate: nil];

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

		return false;
	}
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
598
599
600
601
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
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685


686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724

	[_serverHeaders setObject: value
			   forKey: key];

	return true;
}

- (bool)socket: (OFTCPSocket *)sock
   didReadLine: (OFString *)line
       context: (id)context
     exception: (id)exception
{
	bool ret;

	if (exception != nil) {
		if ([exception isKindOfClass:
		    [OFInvalidEncodingException class]])
			exception = [OFInvalidServerReplyException exception];

		[self raiseException: exception];
		return false;
	}

	@try {
		if (_firstLine) {
			_firstLine = false;
			ret = [self handleFirstLine: line];
		} else
			ret = [self handleServerHeader: line
						socket: sock];
	} @catch (id e) {
		[self raiseException: e];
		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;
		}




		[self raiseException: exception];
		return 0;
	}





	_firstLine = true;




	if ([[_request headers] objectForKey: @"Content-Length"] != nil) {


		OFStream *requestBody = [[[OFHTTPClientRequestBodyStream alloc]
		    initWithHandler: self
			     socket: sock] autorelease];

		if ([_client->_delegate respondsToSelector:
		    @selector(client:wantsRequestBody:request:context:)])
			[_client->_delegate client: _client
				  wantsRequestBody: requestBody
					   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
	 * send it all at once.
	 *
	 * We do not use the socket's write buffer in case we need to resend
	 * the entire request (e.g. in case a keep-alive connection timed out).
	 */

	@try {

		OFString *requestString = constructRequestString(_request);

		/*
		 * Pass requestString as context to retain it so that the
		 * underlying buffer lives long enough.
		 */
		[sock asyncWriteBuffer: [requestString UTF8String]
				length: [requestString UTF8StringLength]
				target: self
			      selector: @selector(socket:didWriteRequest:
					    length:context:exception:)
			       context: requestString];
	} @catch (id e) {
		[self raiseException: e];
		return;
	}
}

- (void)socketDidConnect: (OFTCPSocket *)sock
		 context: (id)context
	       exception: (id)exception
{
	if (exception != nil) {
		[self raiseException: exception];
		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) {
		[self raiseException: exception];
		return false;
	}

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

		[_client->_lastResponse release];
		_client->_lastResponse = nil;

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

	return true;
}

- (void)start
{
	OFURL *URL = [_request URL];
	OFTCPSocket *sock;

	/* Can we reuse the last socket? */
	if (_client->_socket != nil && ![_client->_socket isAtEndOfStream] &&
	    [[_client->_lastURL scheme] isEqual: [URL scheme]] &&
	    [[_client->_lastURL host] isEqual: [URL host]] &&
	    [_client->_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.
		 */
		sock = [_client->_socket autorelease];
		_client->_socket = nil;

		[_client->_lastURL release];
		_client->_lastURL = nil;

		if (!_client->_lastWasHEAD &&
		    ![_client->_lastResponse isAtEndOfStream]) {
			/* Throw away content that has not been read yet */
			char *buffer = [self allocMemoryWithSize: 512];

			[_client->_lastResponse
			    asyncReadIntoBuffer: buffer
					 length: 512
					 target: self
				       selector: @selector(throwAwayContent:
						     buffer:length:context:
						     exception:)
					context: sock];
		} else {
			[_client->_lastResponse release];
			_client->_lastResponse = nil;

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

- (void)closeAndReconnect
{
	@try {







|

<
<



<
<
<
<
<
<
<
<
<















|
<
<
<
|

<
|
|
<
|
|
|
|

>
>
>
|
<
|

>
>
>
>


>
>
>

>
>












|
<
<
<
















>
|

<
<
<
<
|
|
<
<
<
<














>
>













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









|
>
>











<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|

|
|
|
<







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
598
599
600
601
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
656
657
658
659
660
661
662
663














664
665
666
667
668
669

670
671
672
673
674
675
676

	[_serverHeaders setObject: value
			   forKey: key];

	return true;
}

- (bool)stream: (OF_KINDOF(OFStream *))sock
   didReadLine: (OFString *)line


{
	bool ret;










	@try {
		if (_firstLine) {
			_firstLine = false;
			ret = [self handleFirstLine: line];
		} else
			ret = [self handleServerHeader: line
						socket: sock];
	} @catch (id e) {
		[self raiseException: e];
		ret = false;
	}

	return ret;
}

-	  (void)stream: (OF_KINDOF(OFStream *))sock



  didFailWithException: (id)exception
{

	if ([exception isKindOfClass: [OFWriteFailedException class]] &&
	    ([exception errNo] == ECONNRESET || [exception errNo] == EPIPE)) {

		/* In case a keep-alive connection timed out */
		[self closeAndReconnect];
		return;
	}

	if ([exception isKindOfClass: [OFInvalidEncodingException class]])
		exception = [OFInvalidServerReplyException exception];

	[self raiseException: exception];

}

- (size_t)stream: (OF_KINDOF(OFStream *))sock
  didWriteBuffer: (const void **)request
	  length: (size_t)length
{
	_firstLine = true;

	[_requestString release];
	_requestString = nil;

	if ([[_request headers] objectForKey: @"Content-Length"] != nil) {
		[sock setDelegate: nil];

		OFStream *requestBody = [[[OFHTTPClientRequestBodyStream alloc]
		    initWithHandler: self
			     socket: sock] autorelease];

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

	} else
		[sock asyncReadLine];




	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
	 * send it all at once.
	 *
	 * We do not use the socket's write buffer in case we need to resend
	 * the entire request (e.g. in case a keep-alive connection timed out).
	 */

	@try {
		[_requestString release];
		_requestString = [constructRequestString(_request) retain];





		[sock asyncWriteBuffer: [_requestString UTF8String]
				length: [_requestString UTF8StringLength]];




	} @catch (id e) {
		[self raiseException: e];
		return;
	}
}

- (void)socketDidConnect: (OFTCPSocket *)sock
		 context: (id)context
	       exception: (id)exception
{
	if (exception != nil) {
		[self raiseException: exception];
		return;
	}

	[sock setDelegate: self];

	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];
}



























- (void)start
{
	OFURL *URL = [_request URL];
	OFTCPSocket *sock;

	/* Can we reuse the last socket? */
	if (_client->_socket != nil && ![_client->_socket isAtEndOfStream] &&
	    [[_client->_lastURL scheme] isEqual: [URL scheme]] &&
	    [[_client->_lastURL host] isEqual: [URL host]] &&
	    [_client->_lastURL port] == [URL port] &&
	    (_client->_lastWasHEAD ||
	    [_client->_lastResponse isAtEndOfStream])) {
		/*
		 * 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.
		 */
		sock = [_client->_socket autorelease];
		_client->_socket = nil;

		[_client->_lastURL release];
		_client->_lastURL = nil;















		[_client->_lastResponse release];
		_client->_lastResponse = nil;

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

	} else
		[self closeAndReconnect];
}

- (void)closeAndReconnect
{
	@try {
852
853
854
855
856
857
858

859
860
861
862
863
864
865
866
867
868
869
{
	if (_socket == nil)
		return;

	if (_toWrite > 0)
		@throw [OFTruncatedDataException exception];


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

	[_socket release];
	_socket = nil;
}

- (int)fileDescriptorForWriting
{







>
|
<
<
<







804
805
806
807
808
809
810
811
812



813
814
815
816
817
818
819
{
	if (_socket == nil)
		return;

	if (_toWrite > 0)
		@throw [OFTruncatedDataException exception];

	[_socket setDelegate: _handler];
	[_socket asyncReadLine];




	[_socket release];
	_socket = nil;
}

- (int)fileDescriptorForWriting
{

Modified src/OFHTTPServer.m from [7cb45bbee2] to [fe26303808].

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

/*
 * FIXME: Key normalization replaces headers like "DNT" with "Dnt".
 * FIXME: Errors are not reported to the user.
 */

@interface OFHTTPServer ()
- (bool)of_socket: (OF_KINDOF(OFTCPSocket *))sock
  didAcceptSocket: (OF_KINDOF(OFTCPSocket *))clientSocket
	  context: (id)context
	exception: (id)exception;
@end

@interface OFHTTPServerResponse: OFHTTPResponse <OFReadyForWritingObserving>
{
	OF_KINDOF(OFTCPSocket *) _socket;
	OFHTTPServer *_server;
	OFHTTPRequest *_request;
	bool _chunked, _headersSent;
}

- (instancetype)initWithSocket: (OF_KINDOF(OFTCPSocket *))sock
			server: (OFHTTPServer *)server
		       request: (OFHTTPRequest *)request;
@end

@interface OFHTTPServer_Connection: OFObject
{
@public
	OF_KINDOF(OFTCPSocket *) _socket;
	OFHTTPServer *_server;
	OFTimer *_timer;
	enum {
		AWAITING_PROLOG,







|
|

















|







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

/*
 * FIXME: Key normalization replaces headers like "DNT" with "Dnt".
 * FIXME: Errors are not reported to the user.
 */

@interface OFHTTPServer ()
- (bool)of_socket: (OFTCPSocket *)sock
  didAcceptSocket: (OFTCPSocket *)clientSocket
	  context: (id)context
	exception: (id)exception;
@end

@interface OFHTTPServerResponse: OFHTTPResponse <OFReadyForWritingObserving>
{
	OF_KINDOF(OFTCPSocket *) _socket;
	OFHTTPServer *_server;
	OFHTTPRequest *_request;
	bool _chunked, _headersSent;
}

- (instancetype)initWithSocket: (OF_KINDOF(OFTCPSocket *))sock
			server: (OFHTTPServer *)server
		       request: (OFHTTPRequest *)request;
@end

@interface OFHTTPServer_Connection: OFObject <OFStreamDelegate>
{
@public
	OF_KINDOF(OFTCPSocket *) _socket;
	OFHTTPServer *_server;
	OFTimer *_timer;
	enum {
		AWAITING_PROLOG,
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
	OFMutableDictionary *_headers;
	size_t _contentLength;
	OFStream *_requestBody;
}

- (instancetype)initWithSocket: (OF_KINDOF(OFTCPSocket *))sock
			server: (OFHTTPServer *)server;
- (bool)socket: (OF_KINDOF(OFTCPSocket *))sock
   didReadLine: (OFString *)line
       context: (id)context
     exception: (id)exception;
- (bool)parseProlog: (OFString *)line;
- (bool)parseHeaders: (OFString *)line;
- (bool)sendErrorAndClose: (short)statusCode;
- (void)createResponse;
@end

@interface OFHTTPServerRequestBodyStream: OFStream <OFReadyForReadingObserving>







<
<
<
<







88
89
90
91
92
93
94




95
96
97
98
99
100
101
	OFMutableDictionary *_headers;
	size_t _contentLength;
	OFStream *_requestBody;
}

- (instancetype)initWithSocket: (OF_KINDOF(OFTCPSocket *))sock
			server: (OFHTTPServer *)server;




- (bool)parseProlog: (OFString *)line;
- (bool)parseHeaders: (OFString *)line;
- (bool)sendErrorAndClose: (short)statusCode;
- (void)createResponse;
@end

@interface OFHTTPServerRequestBodyStream: OFStream <OFReadyForReadingObserving>
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
	[_path release];
	[_headers release];
	[_requestBody release];

	[super dealloc];
}

- (bool)socket: (OF_KINDOF(OFTCPSocket *))sock
   didReadLine: (OFString *)line
       context: (id)context
     exception: (id)exception
{
	if (line == nil || exception != nil)
		return false;

	@try {
		switch (_state) {
		case AWAITING_PROLOG:
			return [self parseProlog: line];
		case PARSING_HEADERS:







|

<
<

|







401
402
403
404
405
406
407
408
409


410
411
412
413
414
415
416
417
418
	[_path release];
	[_headers release];
	[_requestBody release];

	[super dealloc];
}

- (bool)stream: (OF_KINDOF(OFStream *))sock
   didReadLine: (OFString *)line


{
	if (line == nil)
		return false;

	@try {
		switch (_state) {
		case AWAITING_PROLOG:
			return [self parseProlog: line];
		case PARSING_HEADERS:
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
- (void)stop
{
	[_listeningSocket cancelAsyncRequests];
	[_listeningSocket release];
	_listeningSocket = nil;
}

- (bool)of_socket: (OF_KINDOF(OFTCPSocket *))sock
  didAcceptSocket: (OF_KINDOF(OFTCPSocket *))clientSocket
	  context: (id)context
	exception: (id)exception
{
	OFHTTPServer_Connection *connection;

	if (exception != nil) {
		if ([_delegate respondsToSelector:
		    @selector(server:didReceiveExceptionOnListeningSocket:)])
			return [_delegate		  server: self
			    didReceiveExceptionOnListeningSocket: exception];

		return false;
	}

	connection = [[[OFHTTPServer_Connection alloc]
	    initWithSocket: clientSocket
		    server: self] autorelease];


	[clientSocket asyncReadLineWithTarget: connection
				     selector: @selector(socket:didReadLine:
						  context:exception:)
				      context: nil];

	return true;
}
@end







|
|


















>
|
<
<
<




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
- (void)stop
{
	[_listeningSocket cancelAsyncRequests];
	[_listeningSocket release];
	_listeningSocket = nil;
}

- (bool)of_socket: (OFTCPSocket *)sock
  didAcceptSocket: (OFTCPSocket *)clientSocket
	  context: (id)context
	exception: (id)exception
{
	OFHTTPServer_Connection *connection;

	if (exception != nil) {
		if ([_delegate respondsToSelector:
		    @selector(server:didReceiveExceptionOnListeningSocket:)])
			return [_delegate		  server: self
			    didReceiveExceptionOnListeningSocket: exception];

		return false;
	}

	connection = [[[OFHTTPServer_Connection alloc]
	    initWithSocket: clientSocket
		    server: self] autorelease];

	[clientSocket setDelegate: connection];
	[clientSocket asyncReadLine];




	return true;
}
@end

Modified src/OFRunLoop+Private.h from [f327edb513] to [02b97312cb].

30
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
+ (void)of_setMainRunLoop: (OFRunLoop *)runLoop;
#ifdef OF_HAVE_SOCKETS
+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
				      stream
			  buffer: (void *)buffer
			  length: (size_t)length
			    mode: (of_run_loop_mode_t)mode
			  target: (id)target
			selector: (SEL)selector
			 context: (nullable id)context;
+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
				      stream
			  buffer: (void *)buffer
		     exactLength: (size_t)length
			    mode: (of_run_loop_mode_t)mode
			  target: (id)target
			selector: (SEL)selector
			 context: (nullable id)context;
+ (void)of_addAsyncReadLineForStream: (OFStream <OFReadyForReadingObserving> *)
					  stream
			    encoding: (of_string_encoding_t)encoding
				mode: (of_run_loop_mode_t)mode
			      target: (id)target
			    selector: (SEL)selector
			     context: (nullable id)context;
+ (void)of_addAsyncWriteForStream: (OFStream <OFReadyForWritingObserving> *)
				       stream
			   buffer: (const void *)buffer
			   length: (size_t)length
			     mode: (of_run_loop_mode_t)mode
			   target: (id)target
			 selector: (SEL)selector
			  context: (nullable id)context;
+ (void)of_addAsyncConnectForTCPSocket: (OFTCPSocket *)socket
				  mode: (of_run_loop_mode_t)mode
				target: (id)target
			      selector: (SEL)selector
			       context: (nullable id)context;
+ (void)of_addAsyncAcceptForTCPSocket: (OFTCPSocket *)socket
				 mode: (of_run_loop_mode_t)mode







|
<
<





|
<
<




|
<
<





|
<
<







30
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
+ (void)of_setMainRunLoop: (OFRunLoop *)runLoop;
#ifdef OF_HAVE_SOCKETS
+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
				      stream
			  buffer: (void *)buffer
			  length: (size_t)length
			    mode: (of_run_loop_mode_t)mode
			delegate: (id <OFStreamDelegate>)delegate;


+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
				      stream
			  buffer: (void *)buffer
		     exactLength: (size_t)length
			    mode: (of_run_loop_mode_t)mode
			delegate: (id <OFStreamDelegate>)delegate;


+ (void)of_addAsyncReadLineForStream: (OFStream <OFReadyForReadingObserving> *)
					  stream
			    encoding: (of_string_encoding_t)encoding
				mode: (of_run_loop_mode_t)mode
			    delegate: (id <OFStreamDelegate>)delegate;


+ (void)of_addAsyncWriteForStream: (OFStream <OFReadyForWritingObserving> *)
				       stream
			   buffer: (const void *)buffer
			   length: (size_t)length
			     mode: (of_run_loop_mode_t)mode
			 delegate: (id <OFStreamDelegate>)delegate;


+ (void)of_addAsyncConnectForTCPSocket: (OFTCPSocket *)socket
				  mode: (of_run_loop_mode_t)mode
				target: (id)target
			      selector: (SEL)selector
			       context: (nullable id)context;
+ (void)of_addAsyncAcceptForTCPSocket: (OFTCPSocket *)socket
				 mode: (of_run_loop_mode_t)mode

Modified src/OFRunLoop.m from [b99d9d1539] to [692cbbd5b1].

70
71
72
73
74
75
76


77
78
79
80
81
82
83
}
@end

#ifdef OF_HAVE_SOCKETS
@interface OFRunLoop_QueueItem: OFObject
{
@public


	id _target;
	SEL _selector;
	id _context;
}

- (bool)handleObject: (id)object;
@end







>
>







70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
}
@end

#ifdef OF_HAVE_SOCKETS
@interface OFRunLoop_QueueItem: OFObject
{
@public
	id _delegate;
	/* TODO: Remove once everything is moved to using delegates */
	id _target;
	SEL _selector;
	id _context;
}

- (bool)handleObject: (id)object;
@end
296
297
298
299
300
301
302

303
304
305
306
307
308
309
- (bool)handleObject: (id)object
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)dealloc
{

	[_target release];
	[_context release];

	[super dealloc];
}
@end








>







298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
- (bool)handleObject: (id)object
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)dealloc
{
	[_delegate release];
	[_target release];
	[_context release];

	[super dealloc];
}
@end

322
323
324
325
326
327
328




329
330



331



332
333
334

335
336
337
338
339
340
341
	}

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		return _block(object, _buffer, length, exception);
	else {
# endif




		bool (*func)(id, SEL, OFStream *, void *, size_t, id, id) =
		    (bool (*)(id, SEL, OFStream *, void *, size_t, id, id))



		    [_target methodForSelector: _selector];




		return func(_target, _selector, object, _buffer, length,
		    _context, exception);

# ifdef OF_HAVE_BLOCKS
	}
# endif
}

# ifdef OF_HAVE_BLOCKS
- (void)dealloc







>
>
>
>
|
|
>
>
>
|
>
>
>

|
<
>







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
	}

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		return _block(object, _buffer, length, exception);
	else {
# endif
		if (exception == nil) {
			if (![_delegate respondsToSelector:
			    @selector(stream:didReadIntoBuffer:length:)])
				return false;

			return [_delegate stream: object
			       didReadIntoBuffer: _buffer
					  length: length];
		} else {
			if ([_delegate respondsToSelector:
			    @selector(stream:didFailWithException:)])
				[_delegate	  stream: object
				    didFailWithException: exception];

			return false;

		}
# ifdef OF_HAVE_BLOCKS
	}
# endif
}

# ifdef OF_HAVE_BLOCKS
- (void)dealloc
372
373
374
375
376
377
378
379
380
381


382


383
384
385
386
387
388








389
390
391
392
393
394
395
		if (!_block(object, _buffer, _readLength, exception))
			return false;

		_readLength = 0;
		return true;
	} else {
# endif
		bool (*func)(id, SEL, OFStream *, void *, size_t, id, id) =
		    (bool (*)(id, SEL, OFStream *, void *, size_t, id, id))
		    [_target methodForSelector: _selector];





		if (!func(_target, _selector, object, _buffer, _readLength,
		    _context, exception))
			return false;

		_readLength = 0;
		return true;








# ifdef OF_HAVE_BLOCKS
	}
# endif
}

# ifdef OF_HAVE_BLOCKS
- (void)dealloc







|
<
|
>
>

>
>
|
<
|

|
|
>
>
>
>
>
>
>
>







385
386
387
388
389
390
391
392

393
394
395
396
397
398
399

400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
		if (!_block(object, _buffer, _readLength, exception))
			return false;

		_readLength = 0;
		return true;
	} else {
# endif
		if (exception == nil) {

			if (![_delegate respondsToSelector:
			    @selector(stream:didReadIntoBuffer:length:)])
				return false;

			if (![_delegate stream: object
			     didReadIntoBuffer: _buffer
					length: _readLength])

				return false;

			_readLength = 0;
			return true;
		} else {
			if ([_delegate respondsToSelector:
			    @selector(stream:didFailWithException:)])
				[_delegate	  stream: object
				    didFailWithException: exception];

			return false;
		}
# ifdef OF_HAVE_BLOCKS
	}
# endif
}

# ifdef OF_HAVE_BLOCKS
- (void)dealloc
418
419
420
421
422
423
424




425
426


427



428
429
430

431
432
433
434
435
436
437
		return true;

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		return _block(object, line, exception);
	else {
# endif




		bool (*func)(id, SEL, OFStream *, OFString *, id, id) =
		    (bool (*)(id, SEL, OFStream *, OFString *, id, id))


		    [_target methodForSelector: _selector];




		return func(_target, _selector, object, line, _context,
		    exception);

# ifdef OF_HAVE_BLOCKS
	}
# endif
}

# ifdef OF_HAVE_BLOCKS
- (void)dealloc







>
>
>
>
|
|
>
>
|
>
>
>

|
<
>







441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461

462
463
464
465
466
467
468
469
		return true;

# ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		return _block(object, line, exception);
	else {
# endif
		if (exception == nil) {
			if (![_delegate respondsToSelector:
			    @selector(stream:didReadLine:)])
				return false;

			return [_delegate stream: object
				     didReadLine: line];
		} else {
			if ([_delegate respondsToSelector:
			    @selector(stream:didFailWithException:)])
				[_delegate	  stream: object
				    didFailWithException: exception];

			return false;

		}
# ifdef OF_HAVE_BLOCKS
	}
# endif
}

# ifdef OF_HAVE_BLOCKS
- (void)dealloc
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
		if (_length == 0)
			return false;

		_writtenLength = 0;
		return true;
	} else {
# endif
		bool (*func)(id, SEL, OFStream *, const void *, size_t, id,
		    id) = (bool (*)(id, SEL, OFStream *, const void *, size_t,
		    id, id))[_target methodForSelector: _selector];


		_length = func(_target, _selector, object, &_buffer,

		    _writtenLength, _context, exception);

		if (_length == 0)
			return false;

		_writtenLength = 0;
		return true;








# ifdef OF_HAVE_BLOCKS
	}
# endif
}

# ifdef OF_HAVE_BLOCKS
- (void)dealloc







|
|
|
>

|
>
|

|
|

|
|
>
>
>
>
>
>
>
>







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
		if (_length == 0)
			return false;

		_writtenLength = 0;
		return true;
	} else {
# endif
		if (exception == nil) {
			if (![_delegate respondsToSelector:
			    @selector(stream:didWriteBuffer:length:)])
				return false;

			_length = [_delegate stream: object
				     didWriteBuffer: &_buffer
					     length: _length];

			if (_length == 0)
				return false;

			_writtenLength = 0;
			return true;
		} else {
			if ([_delegate respondsToSelector:
			    @selector(stream:didFailWithException:)])
				[_delegate	  stream: object
				    didFailWithException: exception];

			return false;
		}
# ifdef OF_HAVE_BLOCKS
	}
# endif
}

# ifdef OF_HAVE_BLOCKS
- (void)dealloc
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
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
	objc_autoreleasePoolPop(pool);

+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
				      stream
			  buffer: (void *)buffer
			  length: (size_t)length
			    mode: (of_run_loop_mode_t)mode
			  target: (id)target
			selector: (SEL)selector
			 context: (id)context
{
	ADD_READ(OFRunLoop_ReadQueueItem, stream, mode, {
		queueItem->_target = [target retain];
		queueItem->_selector = selector;
		queueItem->_context = [context retain];
		queueItem->_buffer = buffer;
		queueItem->_length = length;
	})
}

+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
				      stream
			  buffer: (void *)buffer
		     exactLength: (size_t)exactLength
			    mode: (of_run_loop_mode_t)mode
			  target: (id)target
			selector: (SEL)selector
			 context: (id)context
{
	ADD_READ(OFRunLoop_ExactReadQueueItem, stream, mode, {
		queueItem->_target = [target retain];
		queueItem->_selector = selector;
		queueItem->_context = [context retain];
		queueItem->_buffer = buffer;
		queueItem->_exactLength = exactLength;
	})
}

+ (void)of_addAsyncReadLineForStream: (OFStream <OFReadyForReadingObserving> *)
					  stream
			    encoding: (of_string_encoding_t)encoding
				mode: (of_run_loop_mode_t)mode
			      target: (id)target
			    selector: (SEL)selector
			     context: (id)context
{
	ADD_READ(OFRunLoop_ReadLineQueueItem, stream, mode, {
		queueItem->_target = [target retain];
		queueItem->_selector = selector;
		queueItem->_context = [context retain];
		queueItem->_encoding = encoding;
	})
}

+ (void)of_addAsyncWriteForStream: (OFStream <OFReadyForWritingObserving> *)
				       stream
			   buffer: (const void *)buffer
			   length: (size_t)length
			     mode: (of_run_loop_mode_t)mode
			   target: (id)target
			 selector: (SEL)selector
			  context: (id)context
{
	ADD_WRITE(OFRunLoop_WriteQueueItem, stream, mode, {
		queueItem->_target = [target retain];
		queueItem->_selector = selector;
		queueItem->_context = [context retain];
		queueItem->_buffer = buffer;
		queueItem->_length = length;
	})
}

+ (void)of_addAsyncConnectForTCPSocket: (OFTCPSocket *)stream
				  mode: (of_run_loop_mode_t)mode







|
<
<


<
|
<










|
<
<


<
|
<









|
<
<


<
|
<









|
<
<


<
|
<







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
	objc_autoreleasePoolPop(pool);

+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
				      stream
			  buffer: (void *)buffer
			  length: (size_t)length
			    mode: (of_run_loop_mode_t)mode
			delegate: (id <OFStreamDelegate>)delegate


{
	ADD_READ(OFRunLoop_ReadQueueItem, stream, mode, {

		queueItem->_delegate = [delegate retain];

		queueItem->_buffer = buffer;
		queueItem->_length = length;
	})
}

+ (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)
				      stream
			  buffer: (void *)buffer
		     exactLength: (size_t)exactLength
			    mode: (of_run_loop_mode_t)mode
			delegate: (id <OFStreamDelegate>)delegate


{
	ADD_READ(OFRunLoop_ExactReadQueueItem, stream, mode, {

		queueItem->_delegate = [delegate retain];

		queueItem->_buffer = buffer;
		queueItem->_exactLength = exactLength;
	})
}

+ (void)of_addAsyncReadLineForStream: (OFStream <OFReadyForReadingObserving> *)
					  stream
			    encoding: (of_string_encoding_t)encoding
				mode: (of_run_loop_mode_t)mode
			    delegate: (id <OFStreamDelegate>)delegate


{
	ADD_READ(OFRunLoop_ReadLineQueueItem, stream, mode, {

		queueItem->_delegate = [delegate retain];

		queueItem->_encoding = encoding;
	})
}

+ (void)of_addAsyncWriteForStream: (OFStream <OFReadyForWritingObserving> *)
				       stream
			   buffer: (const void *)buffer
			   length: (size_t)length
			     mode: (of_run_loop_mode_t)mode
			 delegate: (id <OFStreamDelegate>)delegate


{
	ADD_WRITE(OFRunLoop_WriteQueueItem, stream, mode, {

		queueItem->_delegate = [delegate retain];

		queueItem->_buffer = buffer;
		queueItem->_length = length;
	})
}

+ (void)of_addAsyncConnectForTCPSocket: (OFTCPSocket *)stream
				  mode: (of_run_loop_mode_t)mode

Modified src/OFStream.h from [e5256c18dc] to [aea2292c00].

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
90
91
92
93
94
95
/*! @file */

@class OFStream;
@class OFData;

#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_BLOCKS)
/*!
 * @brief A block which is called when data was read from the stream.

 *
 * @param stream The stream on which data was read
 * @param buffer A buffer with the data that has been read
 * @param length The length of the data that has been read
 * @param exception An exception which occurred while reading or `nil` on
 *		    success
 * @return A bool whether the same block should be used for the next read
 */
typedef bool (^of_stream_async_read_block_t)(OF_KINDOF(OFStream *) stream,
    void *buffer, size_t length, id _Nullable exception);

/*!
 * @brief A block which is called when a line was read from the stream.

 *
 * @param stream The stream on which a line was read
 * @param line The line which has been read or `nil` when the end of stream
 *	       occurred
 * @param exception An exception which occurred while reading or `nil` on
 *		    success
 * @return A bool whether the same block should be used for the next read
 */
typedef bool (^of_stream_async_read_line_block_t)(OF_KINDOF(OFStream *) stream,
    OFString *_Nullable line, id _Nullable exception);

/*!
 * @brief A block which is called when data was written to the stream.

 *
 * @param stream The stream to which data was written
 * @param buffer A pointer to the buffer which was written to the stream. This
 *		 can be changed to point to a different buffer to be used on the
 *		 next write.
 * @param bytesWritten The number of bytes which have been written. This
 *		       matches the length specified on the asynchronous write
 *		       if no exception was encountered.
 * @param exception An exception which occurred while writing or `nil` on
 *		    success
 * @return The length to repeat the write with or 0 if it should not repeat.
 *	   The buffer may be changed, so that every time a new buffer and length
 *	   can be specified while the callback stays the same.
 */
typedef size_t (^of_stream_async_write_block_t)(OF_KINDOF(OFStream *) stream,
    const void *_Nonnull *_Nonnull buffer, size_t bytesWritten,
    id _Nullable exception);
#endif





























































/*!
 * @class OFStream OFStream.h ObjFW/OFStream.h
 *
 * @brief A base class for different types of streams.
 *
 * @warning Even though the OFCopying protocol is implemented, it does *not*
 *	    return an independent copy of the stream, but instead retains it.







|
>












|
>












|
>












|






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







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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*! @file */

@class OFStream;
@class OFData;

#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_BLOCKS)
/*!
 * @brief A block which is called when data was read asynchronously from a
 *	  stream.
 *
 * @param stream The stream on which data was read
 * @param buffer A buffer with the data that has been read
 * @param length The length of the data that has been read
 * @param exception An exception which occurred while reading or `nil` on
 *		    success
 * @return A bool whether the same block should be used for the next read
 */
typedef bool (^of_stream_async_read_block_t)(OF_KINDOF(OFStream *) stream,
    void *buffer, size_t length, id _Nullable exception);

/*!
 * @brief A block which is called when a line was read asynchronously from a
 *	  stream.
 *
 * @param stream The stream on which a line was read
 * @param line The line which has been read or `nil` when the end of stream
 *	       occurred
 * @param exception An exception which occurred while reading or `nil` on
 *		    success
 * @return A bool whether the same block should be used for the next read
 */
typedef bool (^of_stream_async_read_line_block_t)(OF_KINDOF(OFStream *) stream,
    OFString *_Nullable line, id _Nullable exception);

/*!
 * @brief A block which is called when data was written asynchronously to a
 *	  stream.
 *
 * @param stream The stream to which data was written
 * @param buffer A pointer to the buffer which was written to the stream. This
 *		 can be changed to point to a different buffer to be used on the
 *		 next write.
 * @param bytesWritten The number of bytes which have been written. This
 *		       matches the length specified on the asynchronous write
 *		       if no exception was encountered.
 * @param exception An exception which occurred while writing or `nil` on
 *		    success
 * @return The length to repeat the write with or 0 if it should not repeat.
 *	   The buffer may be changed, so that every time a new buffer and length
 *	   can be specified
 */
typedef size_t (^of_stream_async_write_block_t)(OF_KINDOF(OFStream *) stream,
    const void *_Nonnull *_Nonnull buffer, size_t bytesWritten,
    id _Nullable exception);
#endif

/*!
 * @protocol OFStreamDelegate OFStream.h ObjFW/OFStream.h
 *
 * A delegate for OFStream.
 */
@protocol OFStreamDelegate <OFObject>
@optional
/*!
 * @brief This method is called when data was read asynchronously from the
 *	  stream.
 *
 * @param stream The stream on which data was read
 * @param buffer A buffer with the data that has been read
 * @param length The length of the data that has been read
 * @return A bool whether the read should be repeated
 */
-      (bool)stream: (OF_KINDOF(OFStream *))stream
  didReadIntoBuffer: (void *)buffer
	     length: (size_t)length;

/*!
 * @brief This method is called when a line was read asynchronously from the
 *	  stream.
 *
 * @param stream The stream on which a line was read
 * @param line The line which has been read or `nil` when the end of stream
 *	       occurred
 * @return A bool whether the read should be repeated
 */
- (bool)stream: (OF_KINDOF(OFStream *))stream
   didReadLine: (nullable OFString *)line;

/*!
 * @brief This method is called when data was written asynchronously to the
 *	  stream.
 *
 * @param stream The stream to which data was written
 * @param buffer A pointer to the buffer which was written to the stream. This
 *		 can be changed to point to a different buffer to be used on the
 *		 next write.
 * @param length The length of the buffer that has been written
 * @return The length to repeat the write with or 0 if it should not repeat.
 *	   The buffer may be changed, so that every time a new buffer and
 *	   length can be specified
 */
- (size_t)stream: (OF_KINDOF(OFStream *))stream
  didWriteBuffer: (const void *_Nonnull *_Nonnull)buffer
	  length: (size_t)length;

/*!
 * @brief This method is called when an exception occurred during an
 *	  asynchronous operation on the stream.
 *
 * @param stream The stream for which an exception occurred
 * @param exception The exception which occurred for the stream
 */
-	  (void)stream: (OF_KINDOF(OFStream *))stream
  didFailWithException: (id)exception;
@end

/*!
 * @class OFStream OFStream.h ObjFW/OFStream.h
 *
 * @brief A base class for different types of streams.
 *
 * @warning Even though the OFCopying protocol is implemented, it does *not*
 *	    return an independent copy of the stream, but instead retains it.
113
114
115
116
117
118
119

120
121
122
123
124
125
126
#endif
	char *_Nullable _readBuffer, *_Nullable _readBufferMemory;
	char *_Nullable _writeBuffer;
	size_t _readBufferLength, _writeBufferLength;
	bool _writeBuffered, _waitingForDelimiter;
@protected
	bool _blocking;

}

/*!
 * @brief Whether the end of the stream has been reached.
 */
@property (readonly, nonatomic, getter=isAtEndOfStream) bool atEndOfStream;








>







176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#endif
	char *_Nullable _readBuffer, *_Nullable _readBufferMemory;
	char *_Nullable _writeBuffer;
	size_t _readBufferLength, _writeBufferLength;
	bool _writeBuffered, _waitingForDelimiter;
@protected
	bool _blocking;
	id <OFStreamDelegate> _Nullable _delegate;
}

/*!
 * @brief Whether the end of the stream has been reached.
 */
@property (readonly, nonatomic, getter=isAtEndOfStream) bool atEndOfStream;

138
139
140
141
142
143
144









145
146
147
148
149
150
151
 * @brief Whether the stream is in blocking mode.
 *
 * By default, a stream is in blocking mode.
 * On Win32, setting this currently only works for sockets!
 */
@property (nonatomic, getter=isBlocking) bool blocking;










/*!
 * @brief Reads *at most* size bytes from the stream into a buffer.
 *
 * On network streams, this might read less than the specified number of bytes.
 * If you want to read exactly the specified number of bytes, use
 * @ref readIntoBuffer:exactLength:. Note that a read can even return 0 bytes -
 * this does not necessarily mean that the stream ended, so you still need to







>
>
>
>
>
>
>
>
>







202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
 * @brief Whether the stream is in blocking mode.
 *
 * By default, a stream is in blocking mode.
 * On Win32, setting this currently only works for sockets!
 */
@property (nonatomic, getter=isBlocking) bool blocking;

/*!
 * @brief The delegate for asynchronous operations on the stream.
 *
 * @note The delegate is retained for as long as asynchronous operations are
 *	 still outstanding.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFStreamDelegate> delegate;

/*!
 * @brief Reads *at most* size bytes from the stream into a buffer.
 *
 * On network streams, this might read less than the specified number of bytes.
 * If you want to read exactly the specified number of bytes, use
 * @ref readIntoBuffer:exactLength:. Note that a read can even return 0 bytes -
 * this does not necessarily mean that the stream ended, so you still need to
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
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
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param buffer The buffer into which the data is read.
 *		 The buffer must not be freed before the async read completed!
 * @param length The length of the data that should be read at most.
 *		 The buffer *must* be *at least* this big!
 * @param target The target on which the selector should be called when the
 *		 data has been received. If the method returns true, it will be
 *		 called again with the same buffer and maximum length when more
 *		 data has been received. If you want the next method in the
 *		 queue to handle the data received next, you need to return
 *		 false from the method.
 * @param selector The selector to call on the target. The signature must be
 *		   `bool (OFStream *stream, void *buffer, size_t length,
 *		   id context, id exception)`.
 * @param context A context object to pass along to the target
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		     target: (id)target
		   selector: (SEL)selector
		    context: (nullable id)context;

/*!
 * @brief Asynchronously reads *at most* size bytes from the stream into a
 *	  buffer.
 *
 * On network streams, this might read less than the specified number of bytes.
 * If you want to read exactly the specified number of bytes, use
 * @ref asyncReadIntoBuffer:exactLength:target:selector:context:. Note that a
 * read can even return 0 bytes - this does not necessarily mean that the
 * stream ended, so you still need to check @ref atEndOfStream.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param buffer The buffer into which the data is read.
 *		 The buffer must not be freed before the async read completed!
 * @param length The length of the data that should be read at most.
 *		 The buffer *must* be *at least* this big!
 * @param runLoopMode The run loop mode in which to perform the async read
 * @param target The target on which the selector should be called when the
 *		 data has been received. If the method returns true, it will be
 *		 called again with the same buffer and maximum length when more
 *		 data has been received. If you want the next method in the
 *		 queue to handle the data received next, you need to return
 *		 false from the method.
 * @param selector The selector to call on the target. The signature must be
 *		   `bool (OFStream *stream, void *buffer, size_t length,
 *		   id context, id exception)`.
 * @param context A context object to pass along to the target
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		runLoopMode: (of_run_loop_mode_t)runLoopMode
		     target: (id)target
		   selector: (SEL)selector
		    context: (nullable id)context;

/*!
 * @brief Asynchronously reads exactly the specified length bytes from the
 *	  stream into a buffer.
 *
 * Unlike @ref asyncReadIntoBuffer:length:target:selector:context:, this method
 * does not call the method when less than the specified length has been read -
 * instead, it waits until it got exactly the specified length, the stream has
 * ended or an exception occurred.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param buffer The buffer into which the data is read
 * @param length The length of the data that should be read.
 *		 The buffer *must* be *at least* this big!
 * @param target The target on which the selector should be called when the
 *		 data has been received. If the method returns true, it will be
 *		 called again with the same buffer and exact length when more
 *		 data has been received. If you want the next method in the
 *		 queue to handle the data received next, you need to return
 *		 false from the method.
 * @param selector The selector to call on the target. The signature must be
 *		   `bool (OFStream *stream, void *buffer, size_t length,
 *		   id context, id exception)`.
 * @param context A context object to pass along to the target
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		     target: (id)target
		   selector: (SEL)selector
		    context: (nullable id)context;

/*!
 * @brief Asynchronously reads exactly the specified length bytes from the
 *	  stream into a buffer.
 *
 * Unlike @ref asyncReadIntoBuffer:length:target:selector:context:, this method
 * does not call the method when less than the specified length has been read -
 * instead, it waits until it got exactly the specified length, the stream has
 * ended or an exception occurred.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param buffer The buffer into which the data is read
 * @param length The length of the data that should be read.
 *		 The buffer *must* be *at least* this big!
 * @param runLoopMode The run loop mode in which to perform the async read
 * @param target The target on which the selector should be called when the
 *		 data has been received. If the method returns true, it will be
 *		 called again with the same buffer and exact length when more
 *		 data has been received. If you want the next method in the
 *		 queue to handle the data received next, you need to return
 *		 false from the method.
 * @param selector The selector to call on the target. The signature must be
 *		   `bool (OFStream *stream, void *buffer, size_t length,
 *		   id context, id exception)`.
 * @param context A context object to pass along to the target
 */
- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		runLoopMode: (of_run_loop_mode_t)runLoopMode
		     target: (id)target
		   selector: (SEL)selector
		    context: (nullable id)context;

# ifdef OF_HAVE_BLOCKS
/*!
 * @brief Asynchronously reads *at most* ref size bytes from the stream into a
 *	  buffer.
 *
 * On network streams, this might read less than the specified number of bytes.







<
<
<
<
<
<
<
<
<
<


|
<
<
<



















<
<
<
<
<
<
<
<
<
<



|
<
<
<
















<
<
<
<
<
<
<
<
<
<


|
<
<
<

















<
<
<
<
<
<
<
<
<
<



|
<
<
<







264
265
266
267
268
269
270










271
272
273



274
275
276
277
278
279
280
281
282
283
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
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param buffer The buffer into which the data is read.
 *		 The buffer must not be freed before the async read completed!
 * @param length The length of the data that should be read at most.
 *		 The buffer *must* be *at least* this big!










 */
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length;




/*!
 * @brief Asynchronously reads *at most* size bytes from the stream into a
 *	  buffer.
 *
 * On network streams, this might read less than the specified number of bytes.
 * If you want to read exactly the specified number of bytes, use
 * @ref asyncReadIntoBuffer:exactLength:target:selector:context:. Note that a
 * read can even return 0 bytes - this does not necessarily mean that the
 * stream ended, so you still need to check @ref atEndOfStream.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param buffer The buffer into which the data is read.
 *		 The buffer must not be freed before the async read completed!
 * @param length The length of the data that should be read at most.
 *		 The buffer *must* be *at least* this big!
 * @param runLoopMode The run loop mode in which to perform the async read










 */
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		runLoopMode: (of_run_loop_mode_t)runLoopMode;




/*!
 * @brief Asynchronously reads exactly the specified length bytes from the
 *	  stream into a buffer.
 *
 * Unlike @ref asyncReadIntoBuffer:length:target:selector:context:, this method
 * does not call the method when less than the specified length has been read -
 * instead, it waits until it got exactly the specified length, the stream has
 * ended or an exception occurred.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param buffer The buffer into which the data is read
 * @param length The length of the data that should be read.
 *		 The buffer *must* be *at least* this big!










 */
- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length;




/*!
 * @brief Asynchronously reads exactly the specified length bytes from the
 *	  stream into a buffer.
 *
 * Unlike @ref asyncReadIntoBuffer:length:target:selector:context:, this method
 * does not call the method when less than the specified length has been read -
 * instead, it waits until it got exactly the specified length, the stream has
 * ended or an exception occurred.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param buffer The buffer into which the data is read
 * @param length The length of the data that should be read.
 *		 The buffer *must* be *at least* this big!
 * @param runLoopMode The run loop mode in which to perform the async read










 */
- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		runLoopMode: (of_run_loop_mode_t)runLoopMode;




# ifdef OF_HAVE_BLOCKS
/*!
 * @brief Asynchronously reads *at most* ref size bytes from the stream into a
 *	  buffer.
 *
 * On network streams, this might read less than the specified number of bytes.
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
#ifdef OF_HAVE_SOCKETS
/*!
 * @brief Asynchronously reads until a newline, `\0`, end of stream or an
 *	  exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param target The target on which to call the selector when the data has
 *		 been received. If the method returns true, it will be called
 *		 again when the next line has been received. If you want the
 *		 next method in the queue to handle the next line, you need to
 *		 return false from the method
 * @param selector The selector to call on the target. The signature must be
 *		   `bool (OFStream *stream, OFString *line, id context,
 *		   id exception)`.
 * @param context A context object to pass along to the target
 */
- (void)asyncReadLineWithTarget: (id)target
		       selector: (SEL)selector
			context: (nullable id)context;

/*!
 * @brief Asynchronously reads with the specified encoding until a newline,
 *	  `\0`, end of stream or an exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param encoding The encoding used by the stream
 * @param target The target on which to call the selector when the data has
 *		 been received. If the method returns true, it will be called
 *		 again when the next line has been received. If you want the
 *		 next method in the queue to handle the next line, you need to
 *		 return false from the method
 * @param selector The selector to call on the target. The signature must be
 *		   `bool (OFStream *stream, OFString *line, id context,
 *		   id exception)`.
 * @param context A context object to pass along to the target
 */
- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding
			   target: (id)target
			 selector: (SEL)selector
			  context: (nullable id)context;

/*!
 * @brief Asynchronously reads with the specified encoding until a newline,
 *	  `\0`, end of stream or an exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param encoding The encoding used by the stream
 * @param runLoopMode The run loop mode in which to perform the async read
 * @param target The target on which to call the selector when the data has
 *		 been received. If the method returns true, it will be called
 *		 again when the next line has been received. If you want the
 *		 next method in the queue to handle the next line, you need to
 *		 return false from the method
 * @param selector The selector to call on the target. The signature must be
 *		   `bool (OFStream *stream, OFString *line, id context,
 *		   id exception)`.
 * @param context A context object to pass along to the target
 */
- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding
		      runLoopMode: (of_run_loop_mode_t)runLoopMode
			   target: (id)target
			 selector: (SEL)selector
			  context: (nullable id)context;

# ifdef OF_HAVE_BLOCKS
/*!
 * @brief Asynchronously reads until a newline, `\0`, end of stream or an
 *	  exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order







<
<
<
<
<
<
<
<
<
<

|
<
<









<
<
<
<
<
<
<
<
<

|
<
<
<










<
<
<
<
<
<
<
<
<


|
<
<
<







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
#ifdef OF_HAVE_SOCKETS
/*!
 * @brief Asynchronously reads until a newline, `\0`, end of stream or an
 *	  exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!










 */
- (void)asyncReadLine;



/*!
 * @brief Asynchronously reads with the specified encoding until a newline,
 *	  `\0`, end of stream or an exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param encoding The encoding used by the stream









 */
- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding;




/*!
 * @brief Asynchronously reads with the specified encoding until a newline,
 *	  `\0`, end of stream or an exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
 *	 for this to work!
 *
 * @param encoding The encoding used by the stream
 * @param runLoopMode The run loop mode in which to perform the async read









 */
- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding
		      runLoopMode: (of_run_loop_mode_t)runLoopMode;




# ifdef OF_HAVE_BLOCKS
/*!
 * @brief Asynchronously reads until a newline, `\0`, end of stream or an
 *	  exception occurs.
 *
 * @note The stream must conform to @ref OFReadyForReadingObserving in order
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param buffer The buffer from which the data is written into the stream. The
 *		 buffer needs to be valid until the write request is completed!
 * @param length The length of the data that should be written
 * @param target The target on which the selector should be called when the
 *		 data has been written. The method should return the length for
 *		 the next write with the same callback or 0 if it should not
 *		 repeat. The buffer may be changed, so that every time a new
 *		 buffer and length can be specified while the callback stays
 *		 the same.
 * @param selector The selector to call on the target. It should return the
 *		   length for the next write with the same callback or 0 if it
 *		   should not repeat. The signature must be `size_t (OFStream
 *		   *stream, const void *buffer, size_t bytesWritten, id
 *		   context, id exception)`.
 * @param context A context object to pass along to the target
 */
- (void)asyncWriteBuffer: (const void *)buffer
		  length: (size_t)length
		  target: (id)target
		selector: (SEL)selector
		 context: (nullable id)context;

/*!
 * @brief Asynchronously writes a buffer into the stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param buffer The buffer from which the data is written into the stream. The
 *		 buffer needs to be valid until the write request is completed!
 * @param length The length of the data that should be written
 * @param runLoopMode The run loop mode in which to perform the async write
 * @param target The target on which the selector should be called when the
 *		 data has been written. The method should return the length for
 *		 the next write with the same callback or 0 if it should not
 *		 repeat. The buffer may be changed, so that every time a new
 *		 buffer and length can be specified while the callback stays
 *		 the same.
 * @param selector The selector to call on the target. It should return the
 *		   length for the next write with the same callback or 0 if it
 *		   should not repeat. The signature must be `size_t (OFStream
 *		   *stream, const void *buffer, size_t bytesWritten, id
 *		   context, id exception)`.
 * @param context A context object to pass along to the target
 */
- (void)asyncWriteBuffer: (const void *)buffer
		  length: (size_t)length
	     runLoopMode: (of_run_loop_mode_t)runLoopMode
		  target: (id)target
		selector: (SEL)selector
		 context: (nullable id)context;

# ifdef OF_HAVE_BLOCKS
/*!
 * @brief Asynchronously writes a buffer into the stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!







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


|
<
<
<











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



|
<
<
<







968
969
970
971
972
973
974












975
976
977



978
979
980
981
982
983
984
985
986
987
988












989
990
991
992



993
994
995
996
997
998
999
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param buffer The buffer from which the data is written into the stream. The
 *		 buffer needs to be valid until the write request is completed!
 * @param length The length of the data that should be written












 */
- (void)asyncWriteBuffer: (const void *)buffer
		  length: (size_t)length;




/*!
 * @brief Asynchronously writes a buffer into the stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!
 *
 * @param buffer The buffer from which the data is written into the stream. The
 *		 buffer needs to be valid until the write request is completed!
 * @param length The length of the data that should be written
 * @param runLoopMode The run loop mode in which to perform the async write












 */
- (void)asyncWriteBuffer: (const void *)buffer
		  length: (size_t)length
	     runLoopMode: (of_run_loop_mode_t)runLoopMode;




# ifdef OF_HAVE_BLOCKS
/*!
 * @brief Asynchronously writes a buffer into the stream.
 *
 * @note The stream must conform to @ref OFReadyForWritingObserving in order
 *	 for this to work!

Modified src/OFStream.m from [66ad6e1874] to [a08f06103a].

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#import "OFWriteFailedException.h"

#import "of_asprintf.h"

#define MIN_READ_SIZE 512

@implementation OFStream
@synthesize of_waitingForDelimiter = _waitingForDelimiter;

#if defined(SIGPIPE) && defined(SIG_IGN)
+ (void)initialize
{
	if (self == [OFStream class])
		signal(SIGPIPE, SIG_IGN);
}







|







59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#import "OFWriteFailedException.h"

#import "of_asprintf.h"

#define MIN_READ_SIZE 512

@implementation OFStream
@synthesize of_waitingForDelimiter = _waitingForDelimiter, delegate = _delegate;

#if defined(SIGPIPE) && defined(SIG_IGN)
+ (void)initialize
{
	if (self == [OFStream class])
		signal(SIGPIPE, SIG_IGN);
}
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
					    length: length - readLength];
	}
}

#ifdef OF_HAVE_SOCKETS
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		     target: (id)target
		   selector: (SEL)selector
		    context: (id)context
{
	[self asyncReadIntoBuffer: buffer
			   length: length
		      runLoopMode: of_run_loop_mode_default
			   target: target
			 selector: selector
			  context: context];
}

- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		runLoopMode: (of_run_loop_mode_t)runLoopMode
		     target: (id)target
		   selector: (SEL)selector
		    context: (id)context
{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadForStream: stream
				     buffer: buffer
				     length: length
				       mode: runLoopMode
				     target: target
				   selector: selector
				    context: context];
}

- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		     target: (id)target
		   selector: (SEL)selector
		    context: (id)context
{
	[self asyncReadIntoBuffer: buffer
		      exactLength: length
		      runLoopMode: of_run_loop_mode_default
			   target: target
			 selector: selector
			  context: context];
}

- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		runLoopMode: (of_run_loop_mode_t)runLoopMode
		     target: (id)target
		   selector: (SEL)selector
		    context: (id)context
{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadForStream: stream
				     buffer: buffer
				exactLength: length
				       mode: runLoopMode
				     target: target
				   selector: selector
				    context: context];
}

# ifdef OF_HAVE_BLOCKS
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		      block: (of_stream_async_read_block_t)block
{







<
<
<



|
<
<
<





<
<
<








<
|
<




<
<
<



|
<
<
<





<
<
<








<
|
<







193
194
195
196
197
198
199



200
201
202
203



204
205
206
207
208



209
210
211
212
213
214
215
216

217

218
219
220
221



222
223
224
225



226
227
228
229
230



231
232
233
234
235
236
237
238

239

240
241
242
243
244
245
246
					    length: length - readLength];
	}
}

#ifdef OF_HAVE_SOCKETS
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length



{
	[self asyncReadIntoBuffer: buffer
			   length: length
		      runLoopMode: of_run_loop_mode_default];



}

- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		runLoopMode: (of_run_loop_mode_t)runLoopMode



{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadForStream: stream
				     buffer: buffer
				     length: length
				       mode: runLoopMode

				   delegate: _delegate];

}

- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length



{
	[self asyncReadIntoBuffer: buffer
		      exactLength: length
		      runLoopMode: of_run_loop_mode_default];



}

- (void)asyncReadIntoBuffer: (void *)buffer
		exactLength: (size_t)length
		runLoopMode: (of_run_loop_mode_t)runLoopMode



{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadForStream: stream
				     buffer: buffer
				exactLength: length
				       mode: runLoopMode

				   delegate: _delegate];

}

# ifdef OF_HAVE_BLOCKS
- (void)asyncReadIntoBuffer: (void *)buffer
		     length: (size_t)length
		      block: (of_stream_async_read_block_t)block
{
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
		if ([self isAtEndOfStream])
			return nil;

	return line;
}

#ifdef OF_HAVE_SOCKETS
- (void)asyncReadLineWithTarget: (id)target
		       selector: (SEL)selector
			context: (id)context
{
	[self asyncReadLineWithEncoding: OF_STRING_ENCODING_UTF_8
			    runLoopMode: of_run_loop_mode_default
				 target: target
			       selector: selector
				context: context];
}

- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding
			   target: (id)target
			 selector: (SEL)selector
			  context: (id)context
{
	[self asyncReadLineWithEncoding: encoding
			    runLoopMode: of_run_loop_mode_default
				 target: target
			       selector: selector
				context: context];
}

- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding
		      runLoopMode: (of_run_loop_mode_t)runLoopMode
			   target: (id)target
			 selector: (SEL)selector
			  context: (id)context
{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadLineForStream: stream
				       encoding: encoding
					   mode: runLoopMode
					 target: target
				       selector: selector
					context: context];
}

# ifdef OF_HAVE_BLOCKS
- (void)asyncReadLineWithBlock: (of_stream_async_read_line_block_t)block
{
	[self asyncReadLineWithEncoding: OF_STRING_ENCODING_UTF_8
			    runLoopMode: of_run_loop_mode_default







|
<
<


|
<
<
<



<
<
<


|
<
<
<




<
<
<







<
|
<







862
863
864
865
866
867
868
869


870
871
872



873
874
875



876
877
878



879
880
881
882



883
884
885
886
887
888
889

890

891
892
893
894
895
896
897
		if ([self isAtEndOfStream])
			return nil;

	return line;
}

#ifdef OF_HAVE_SOCKETS
- (void)asyncReadLine


{
	[self asyncReadLineWithEncoding: OF_STRING_ENCODING_UTF_8
			    runLoopMode: of_run_loop_mode_default];



}

- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding



{
	[self asyncReadLineWithEncoding: encoding
			    runLoopMode: of_run_loop_mode_default];



}

- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding
		      runLoopMode: (of_run_loop_mode_t)runLoopMode



{
	OFStream <OFReadyForReadingObserving> *stream =
	    (OFStream <OFReadyForReadingObserving> *)self;

	[OFRunLoop of_addAsyncReadLineForStream: stream
				       encoding: encoding
					   mode: runLoopMode

				       delegate: _delegate];

}

# ifdef OF_HAVE_BLOCKS
- (void)asyncReadLineWithBlock: (of_stream_async_read_line_block_t)block
{
	[self asyncReadLineWithEncoding: OF_STRING_ENCODING_UTF_8
			    runLoopMode: of_run_loop_mode_default
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
		return length;
	}
}

#ifdef OF_HAVE_SOCKETS
- (void)asyncWriteBuffer: (const void *)buffer
		  length: (size_t)length
		  target: (id)target
		selector: (SEL)selector
		 context: (id)context
{
	[self asyncWriteBuffer: buffer
			length: length
		   runLoopMode: of_run_loop_mode_default
			target: target
		      selector: selector
		       context: context];
}

- (void)asyncWriteBuffer: (const void *)buffer
		  length: (size_t)length
	     runLoopMode: (of_run_loop_mode_t)runLoopMode
		  target: (id)target
		selector: (SEL)selector
		 context: (id)context
{
	OFStream <OFReadyForWritingObserving> *stream =
	    (OFStream <OFReadyForWritingObserving> *)self;

	[OFRunLoop of_addAsyncWriteForStream: stream
				      buffer: buffer
				      length: length
					mode: runLoopMode
				      target: target
				    selector: selector
				     context: context];
}

# ifdef OF_HAVE_BLOCKS
- (void)asyncWriteBuffer: (const void *)buffer
		  length: (size_t)length
		   block: (of_stream_async_write_block_t)block
{







<
<
<



|
<
<
<





<
<
<








<
|
<







1164
1165
1166
1167
1168
1169
1170



1171
1172
1173
1174



1175
1176
1177
1178
1179



1180
1181
1182
1183
1184
1185
1186
1187

1188

1189
1190
1191
1192
1193
1194
1195
		return length;
	}
}

#ifdef OF_HAVE_SOCKETS
- (void)asyncWriteBuffer: (const void *)buffer
		  length: (size_t)length



{
	[self asyncWriteBuffer: buffer
			length: length
		   runLoopMode: of_run_loop_mode_default];



}

- (void)asyncWriteBuffer: (const void *)buffer
		  length: (size_t)length
	     runLoopMode: (of_run_loop_mode_t)runLoopMode



{
	OFStream <OFReadyForWritingObserving> *stream =
	    (OFStream <OFReadyForWritingObserving> *)self;

	[OFRunLoop of_addAsyncWriteForStream: stream
				      buffer: buffer
				      length: length
					mode: runLoopMode

				    delegate: _delegate];

}

# ifdef OF_HAVE_BLOCKS
- (void)asyncWriteBuffer: (const void *)buffer
		  length: (size_t)length
		   block: (of_stream_async_write_block_t)block
{

Modified src/OFTCPSocket.m from [44b284db49] to [df6639e5af].

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
90
91

Class of_tls_socket_class = Nil;

static of_run_loop_mode_t connectRunLoopMode = @"of_tcp_socket_connect_mode";
static OFString *defaultSOCKS5Host = nil;
static uint16_t defaultSOCKS5Port = 1080;

@interface OFTCPSocket_AsyncConnectContext: OFObject
{
	OFTCPSocket *_socket;
	OFString *_host;
	uint16_t _port;
	OFString *_SOCKS5Host;
	uint16_t _SOCKS5Port;
	id _target;
	SEL _selector;
	id _context;
#ifdef OF_HAVE_BLOCKS
	of_tcp_socket_async_connect_block_t _block;
#endif
	id _exception;
	OFData *_socketAddresses;
	size_t _socketAddressesIndex;








	/* Longest read is domain name (max 255 bytes) + port */
	unsigned char _buffer[257];

}

- (instancetype)initWithSocket: (OFTCPSocket *)sock
			  host: (OFString *)host
			  port: (uint16_t)port
		    SOCKS5Host: (OFString *)SOCKS5Host
		    SOCKS5Port: (uint16_t)SOCKS5Port







|















>
>
>
>
>
>
>
>


>







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
90
91
92
93
94
95
96
97
98
99
100

Class of_tls_socket_class = Nil;

static of_run_loop_mode_t connectRunLoopMode = @"of_tcp_socket_connect_mode";
static OFString *defaultSOCKS5Host = nil;
static uint16_t defaultSOCKS5Port = 1080;

@interface OFTCPSocket_AsyncConnectContext: OFObject <OFStreamDelegate>
{
	OFTCPSocket *_socket;
	OFString *_host;
	uint16_t _port;
	OFString *_SOCKS5Host;
	uint16_t _SOCKS5Port;
	id _target;
	SEL _selector;
	id _context;
#ifdef OF_HAVE_BLOCKS
	of_tcp_socket_async_connect_block_t _block;
#endif
	id _exception;
	OFData *_socketAddresses;
	size_t _socketAddressesIndex;
	enum {
		SOCKS5_STATE_SEND_AUTHENTICATION = 1,
		SOCKS5_STATE_READ_VERSION,
		SOCKS5_STATE_SEND_REQUEST,
		SOCKS5_STATE_READ_RESPONSE,
		SOCKS5_STATE_READ_ADDRESS,
		SOCKS5_STATE_READ_ADDRESS_LENGTH,
	} _SOCKS5State;
	/* Longest read is domain name (max 255 bytes) + port */
	unsigned char _buffer[257];
	OFMutableData *_request;
}

- (instancetype)initWithSocket: (OFTCPSocket *)sock
			  host: (OFString *)host
			  port: (uint16_t)port
		    SOCKS5Host: (OFString *)SOCKS5Host
		    SOCKS5Port: (uint16_t)SOCKS5Port
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
-	(void)resolver: (OFDNSResolver *)resolver
  didResolveDomainName: (OFString *)domainName
       socketAddresses: (OFData *)socketAddresses
	       context: (id)context
	     exception: (id)exception;
- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
- (void)sendSOCKS5Request;
-	       (size_t)socket: (OFTCPSocket *)sock
  didSendSOCKS5Authentication: (const void *)request
		 bytesWritten: (size_t)bytesWritten
		      context: (id)context
		    exception: (id)exception;
-	 (bool)socket: (OFTCPSocket *)sock
  didReadSOCKSVersion: (unsigned char *)SOCKSVersion
	       length: (size_t)length
	      context: (id)context
	    exception: (id)exception;
-	(size_t)socket: (OFTCPSocket *)sock
  didSendSOCKS5Request: (const void *)request
	  bytesWritten: (size_t)bytesWritten
	       context: (id)context
	     exception: (id)exception;
-	   (bool)socket: (OFTCPSocket *)sock
  didReadSOCKS5Response: (unsigned char *)response
		 length: (size_t)length
		context: (id)context
	      exception: (id)exception;
-	  (bool)socket: (OFTCPSocket *)sock
  didReadSOCKS5Address: (unsigned char *)address
		length: (size_t)length
	       context: (id)context
	     exception: (id)exception;
-		(bool)socket: (OFTCPSocket *)sock
  didReadSOCKS5AddressLength: (unsigned char *)addressLength
		      length: (size_t)length
		     context: (id)context
		   exception: (id)exception;
@end

@interface OFTCPSocket_ConnectContext: OFObject
{
@public
	bool _done;
	id _exception;







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







117
118
119
120
121
122
123






























124
125
126
127
128
129
130
-	(void)resolver: (OFDNSResolver *)resolver
  didResolveDomainName: (OFString *)domainName
       socketAddresses: (OFData *)socketAddresses
	       context: (id)context
	     exception: (id)exception;
- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
- (void)sendSOCKS5Request;






























@end

@interface OFTCPSocket_ConnectContext: OFObject
{
@public
	bool _done;
	id _exception;
173
174
175
176
177
178
179


180
181
182
183
184
185
186
		_host = [host copy];
		_port = port;
		_SOCKS5Host = [SOCKS5Host copy];
		_SOCKS5Port = SOCKS5Port;
		_target = [target retain];
		_selector = selector;
		_context = [context retain];


	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}







>
>







152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
		_host = [host copy];
		_port = port;
		_SOCKS5Host = [SOCKS5Host copy];
		_SOCKS5Port = SOCKS5Port;
		_target = [target retain];
		_selector = selector;
		_context = [context retain];

		[_socket setDelegate: self];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}
209
210
211
212
213
214
215



216
217
218
219
220
221
222
223
224
225

226
227
228
229
230
231


232
233
234
235
236
237
238

	return self;
}
#endif

- (void)dealloc
{



	[_socket release];
	[_host release];
	[_SOCKS5Host release];
	[_target release];
	[_context release];
#ifdef OF_HAVE_BLOCKS
	[_block release];
#endif
	[_exception release];
	[_socketAddresses release];


	[super dealloc];
}

- (void)didConnect
{


	if (_exception == nil)
		[_socket setBlocking: true];

#ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		_block(_socket, _exception);
	else {







>
>
>










>






>
>







190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225

	return self;
}
#endif

- (void)dealloc
{
	if ([_socket delegate] == self)
		[_socket setDelegate: nil];

	[_socket release];
	[_host release];
	[_SOCKS5Host release];
	[_target release];
	[_context release];
#ifdef OF_HAVE_BLOCKS
	[_block release];
#endif
	[_exception release];
	[_socketAddresses release];
	[_request release];

	[super dealloc];
}

- (void)didConnect
{
	[_socket setDelegate: nil];

	if (_exception == nil)
		[_socket setBlocking: true];

#ifdef OF_HAVE_BLOCKS
	if (_block != NULL)
		_block(_socket, _exception);
	else {
389
390
391
392
393
394
395

396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442



443
444
445
446
447
448
449
450
451
452
453

454

455
456
457
458
459
460
461
462
463
464
465
466
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
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
598
599
600
601
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
						    socketAddresses:context:
						    exception:)
				       context: nil];
}

- (void)sendSOCKS5Request
{

	[_socket asyncWriteBuffer: "\x05\x01\x00"
			   length: 3
		      runLoopMode: [[OFRunLoop currentRunLoop] currentMode]
			   target: self
			 selector: @selector(socket:didSendSOCKS5Authentication:
				       bytesWritten:context:exception:)
			  context: nil];
}

-	       (size_t)socket: (OFTCPSocket *)sock
  didSendSOCKS5Authentication: (const void *)request
		 bytesWritten: (size_t)bytesWritten
		      context: (id)context
		    exception: (id)exception
{
	if (exception != nil) {
		_exception = [exception retain];
		[self didConnect];
		return 0;
	}

	[_socket asyncReadIntoBuffer: _buffer
			 exactLength: 2
			 runLoopMode: [[OFRunLoop currentRunLoop] currentMode]
			      target: self
			    selector: @selector(socket:didReadSOCKSVersion:
					  length:context:exception:)
			     context: nil];

	return 0;
}

-	 (bool)socket: (OFTCPSocket *)sock
  didReadSOCKSVersion: (unsigned char *)SOCKSVersion
	       length: (size_t)length
	      context: (id)context
	    exception: (id)exception
{
	OFMutableData *request;
	uint8_t hostLength;
	unsigned char port[2];

	if (exception != nil) {
		_exception = [exception retain];
		[self didConnect];
		return false;
	}




	if (SOCKSVersion[0] != 5 || SOCKSVersion[1] != 0) {
		_exception = [[OFConnectionFailedException alloc]
		    initWithHost: _host
			    port: _port
			  socket: self
			   errNo: EPROTONOSUPPORT];
		[self didConnect];
		return false;
	}


	request = [OFMutableData data];

	[request addItems: "\x05\x01\x00\x03"
		    count: 4];

	hostLength = (uint8_t)[_host UTF8StringLength];
	[request addItem: &hostLength];
	[request addItems: [_host UTF8String]
		    count: hostLength];

	port[0] = _port >> 8;
	port[1] = _port & 0xFF;
	[request addItems: port
		    count: 2];

	/* Use request as context to retain it */
	[_socket asyncWriteBuffer: [request items]
			   length: [request count]
		      runLoopMode: [[OFRunLoop currentRunLoop] currentMode]
			   target: self
			 selector: @selector(socket:didSendSOCKS5Request:
				       bytesWritten:context:exception:)
			  context: request];

	return false;
}

-	(size_t)socket: (OFTCPSocket *)sock
  didSendSOCKS5Request: (const void *)request
	  bytesWritten: (size_t)bytesWritten
	       context: (id)context
	     exception: (id)exception
{
	if (exception != nil) {
		_exception = [exception retain];
		[self didConnect];
		return 0;
	}

	[_socket asyncReadIntoBuffer: _buffer
			 exactLength: 4
			 runLoopMode: [[OFRunLoop currentRunLoop] currentMode]
			      target: self
			    selector: @selector(socket:didReadSOCKS5Response:
					  length:context:exception:)
			     context: nil];

	return 0;
}

-	   (bool)socket: (OFTCPSocket *)sock
  didReadSOCKS5Response: (unsigned char *)response
		 length: (size_t)length
		context: (id)context
	      exception: (id)exception
{
	of_run_loop_mode_t runLoopMode;

	if (exception != nil) {
		_exception = [exception retain];
		[self didConnect];
		return false;
	}

	if (response[0] != 5 || response[2] != 0) {
		_exception = [[OFConnectionFailedException alloc]
		    initWithHost: _host
			    port: _port
			  socket: self
			   errNo: EPROTONOSUPPORT];
		[self didConnect];
		return false;
	}

	if (response[1] != 0) {
		int errNo;

		switch (response[1]) {
		case 0x02:
			errNo = EACCES;
			break;
		case 0x03:
			errNo = ENETUNREACH;
			break;
		case 0x04:
			errNo = EHOSTUNREACH;
			break;
		case 0x05:
			errNo = ECONNREFUSED;
			break;
		case 0x06:
			errNo = ETIMEDOUT;
			break;
		case 0x07:
			errNo = EPROTONOSUPPORT;
			break;
		case 0x08:
			errNo = EAFNOSUPPORT;
			break;
		default:
			errNo = EPROTO;
			break;
		}

		_exception = [[OFConnectionFailedException alloc]
		    initWithHost: _host
			    port: _port
			  socket: _socket
			   errNo: errNo];
		[self didConnect];
		return false;
	}

	runLoopMode = [[OFRunLoop currentRunLoop] currentMode];

	/* Skip the rest of the response */
	switch (response[3]) {
	case 1: /* IPv4 */

		[_socket asyncReadIntoBuffer: _buffer
				 exactLength: 4 + 2
				 runLoopMode: runLoopMode
				      target: self
				    selector: @selector(socket:
						  didReadSOCKS5Address:length:
						  context:exception:)
				     context: nil];
		return false;
	case 3: /* Domain name */

		[_socket asyncReadIntoBuffer: _buffer
				 exactLength: 1
				 runLoopMode: runLoopMode
				      target: self
				    selector: @selector(socket:
						  didReadSOCKS5AddressLength:
						  length:context:exception:)
				     context: nil];
		return false;
	case 4: /* IPv6 */

		[_socket asyncReadIntoBuffer: _buffer
				 exactLength: 16 + 2
				 runLoopMode: runLoopMode
				      target: self
				    selector: @selector(socket:
						  didReadSOCKS5Address:length:
						  context:exception:)
				     context: nil];
		return false;
	default:
		_exception = [[OFConnectionFailedException alloc]
		    initWithHost: _host
			    port: _port
			  socket: self
			   errNo: EPROTONOSUPPORT];
		[self didConnect];
		return false;
	}

	return false;





}










-	  (bool)socket: (OFTCPSocket *)sock
  didReadSOCKS5Address: (unsigned char *)address


		length: (size_t)length
	       context: (id)context
	     exception: (id)exception
{
	_exception = [exception retain];
	[self didConnect];
	return false;


}



-		(bool)socket: (OFTCPSocket *)sock
  didReadSOCKS5AddressLength: (unsigned char *)addressLength
		      length: (size_t)length
		     context: (id)context
		   exception: (id)exception
{
	if (exception != nil) {
		_exception = [exception retain];
		[self didConnect];
		return false;



	}

	[_socket asyncReadIntoBuffer: _buffer
			 exactLength: addressLength[0] + 2
			 runLoopMode: [[OFRunLoop currentRunLoop] currentMode]
			      target: self
			    selector: @selector(socket:didReadSOCKS5Address:
					  length:context:exception:)
			     context: nil];


	return false;








}
@end

@implementation OFTCPSocket_ConnectContext
- (void)dealloc
{
	[_exception release];







>


|
<
<
<
<


|
|
|
<
<

<
<
<
<
<
|
<
<
|
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<


|
<
<
<
<
|
>
>
>

|
|
|
|
|
|
|
|
|

>
|
>
|
|

|
|
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
|
|
|
|
|
|
|
|
<
<
<
<
<
|
<
|
<
<
<
<
<
<
<
|
<
<
<
<
<

|
|
|
|
|
|
|
|
|

|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|

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

|
<
<
<
<
<
|
|
|
|
|
|
|
|
|
|

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

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







376
377
378
379
380
381
382
383
384
385
386




387
388
389
390
391


392





393


394









395





396
397
398




399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423























424
425
426
427
428
429
430
431
432
433





434

435







436





437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
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
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
						    socketAddresses:context:
						    exception:)
				       context: nil];
}

- (void)sendSOCKS5Request
{
	_SOCKS5State = SOCKS5_STATE_SEND_AUTHENTICATION;
	[_socket asyncWriteBuffer: "\x05\x01\x00"
			   length: 3
		      runLoopMode: [[OFRunLoop currentRunLoop] currentMode]];




}

-      (bool)stream: (OF_KINDOF(OFStream *))sock
  didReadIntoBuffer: (void *)buffer
	     length: (size_t)length


{





	of_run_loop_mode_t runLoopMode =


	    [[OFRunLoop currentRunLoop] currentMode];









	unsigned char *SOCKSVersion;





	uint8_t hostLength;
	unsigned char port[2];
	unsigned char *response, *addressLength;





	switch (_SOCKS5State) {
	case SOCKS5_STATE_READ_VERSION:
		SOCKSVersion = buffer;

		if (SOCKSVersion[0] != 5 || SOCKSVersion[1] != 0) {
			_exception = [[OFConnectionFailedException alloc]
			    initWithHost: _host
				    port: _port
				  socket: self
				   errNo: EPROTONOSUPPORT];
			[self didConnect];
			return false;
		}

		[_request release];
		_request = [[OFMutableData alloc] init];

		[_request addItems: "\x05\x01\x00\x03"
			     count: 4];

		hostLength = (uint8_t)[_host UTF8StringLength];
		[_request addItem: &hostLength];
		[_request addItems: [_host UTF8String]
			     count: hostLength];
























		port[0] = _port >> 8;
		port[1] = _port & 0xFF;
		[_request addItems: port
			    count: 2];

		_SOCKS5State = SOCKS5_STATE_SEND_REQUEST;
		[_socket asyncWriteBuffer: [_request items]
				   length: [_request count]
			      runLoopMode: runLoopMode];





		return false;

	case SOCKS5_STATE_READ_RESPONSE:







		response = buffer;






		if (response[0] != 5 || response[2] != 0) {
			_exception = [[OFConnectionFailedException alloc]
			    initWithHost: _host
				    port: _port
				  socket: self
				   errNo: EPROTONOSUPPORT];
			[self didConnect];
			return false;
		}

		if (response[1] != 0) {
			int errNo;

			switch (response[1]) {
			case 0x02:
				errNo = EACCES;
				break;
			case 0x03:
				errNo = ENETUNREACH;
				break;
			case 0x04:
				errNo = EHOSTUNREACH;
				break;
			case 0x05:
				errNo = ECONNREFUSED;
				break;
			case 0x06:
				errNo = ETIMEDOUT;
				break;
			case 0x07:
				errNo = EPROTONOSUPPORT;
				break;
			case 0x08:
				errNo = EAFNOSUPPORT;
				break;
			default:
				errNo = EPROTO;
				break;
			}

			_exception = [[OFConnectionFailedException alloc]
			    initWithHost: _host
				    port: _port
				  socket: _socket
				   errNo: errNo];
			[self didConnect];
			return false;
		}



		/* Skip the rest of the response */
		switch (response[3]) {
		case 1: /* IPv4 */
			_SOCKS5State = SOCKS5_STATE_READ_ADDRESS;
			[_socket asyncReadIntoBuffer: _buffer
					 exactLength: 4 + 2
					 runLoopMode: runLoopMode];





			return false;
		case 3: /* Domain name */
			_SOCKS5State = SOCKS5_STATE_READ_ADDRESS_LENGTH;
			[_socket asyncReadIntoBuffer: _buffer
					 exactLength: 1
					 runLoopMode: runLoopMode];





			return false;
		case 4: /* IPv6 */
			_SOCKS5State = SOCKS5_STATE_READ_ADDRESS;
			[_socket asyncReadIntoBuffer: _buffer
				 exactLength: 16 + 2
				 runLoopMode: runLoopMode];





			return false;
		default:
			_exception = [[OFConnectionFailedException alloc]
			    initWithHost: _host
				    port: _port
				  socket: self
				   errNo: EPROTONOSUPPORT];
			[self didConnect];
			return false;
		}

		return false;
	case SOCKS5_STATE_READ_ADDRESS:
		[self didConnect];
		return false;
	case SOCKS5_STATE_READ_ADDRESS_LENGTH:
		addressLength = buffer;

		_SOCKS5State = SOCKS5_STATE_READ_ADDRESS;
		[_socket asyncReadIntoBuffer: _buffer
				 exactLength: addressLength[0] + 2
				 runLoopMode: runLoopMode];
		return false;
	default:
		assert(0);
		return false;
	}
}


- (size_t)stream: (OF_KINDOF(OFStream *))sock
  didWriteBuffer: (const void **)buffer
	  length: (size_t)length


{



	of_run_loop_mode_t runLoopMode =
	    [[OFRunLoop currentRunLoop] currentMode];

	switch (_SOCKS5State) {
	case SOCKS5_STATE_SEND_AUTHENTICATION:
		_SOCKS5State = SOCKS5_STATE_READ_VERSION;
		[_socket asyncReadIntoBuffer: _buffer

				 exactLength: 2
				 runLoopMode: runLoopMode];





		return 0;
	case SOCKS5_STATE_SEND_REQUEST:
		[_request release];
		_request = nil;

		_SOCKS5State = SOCKS5_STATE_READ_RESPONSE;
		[_socket asyncReadIntoBuffer: _buffer
				 exactLength: 4
				 runLoopMode: runLoopMode];
		return 0;



	default:
		assert(0);
		return 0;
	}
}

-	  (void)stream: (OF_KINDOF(OFStream *))sock
  didFailWithException: (id)exception
{
	_exception = [exception retain];
	[self didConnect];
}
@end

@implementation OFTCPSocket_ConnectContext
- (void)dealloc
{
	[_exception release];

Modified utils/ofhttp/OFHTTP.m from [661d5ad21b] to [31817d3b24].

49
50
51
52
53
54
55
56

57
58
59
60
61
62
63

#import "ProgressBar.h"

#define GIBIBYTE (1024 * 1024 * 1024)
#define MEBIBYTE (1024 * 1024)
#define KIBIBYTE (1024)

@interface OFHTTP: OFObject <OFApplicationDelegate, OFHTTPClientDelegate>

{
	OFArray OF_GENERIC(OFString *) *_URLs;
	size_t _URLIndex;
	int _errorCode;
	OFString *_outputPath, *_currentFileName;
	bool _continue, _force, _detectFileName, _detectedFileName;
	bool _quiet, _verbose, _insecure;







|
>







49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

#import "ProgressBar.h"

#define GIBIBYTE (1024 * 1024 * 1024)
#define MEBIBYTE (1024 * 1024)
#define KIBIBYTE (1024)

@interface OFHTTP: OFObject <OFApplicationDelegate, OFHTTPClientDelegate,
    OFStreamDelegate>
{
	OFArray OF_GENERIC(OFString *) *_URLs;
	size_t _URLIndex;
	int _errorCode;
	OFString *_outputPath, *_currentFileName;
	bool _continue, _force, _detectFileName, _detectedFileName;
	bool _quiet, _verbose, _insecure;
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704


705
706
707
708
709






















710
711
712
713
714
715
716
717
718
719
	} else
		@throw e;

	[self performSelector: @selector(downloadNextURL)
		   afterDelay: 0];
}

-      (bool)stream: (OFHTTPResponse *)response
  didReadIntoBuffer: (void *)buffer
	     length: (size_t)length
	    context: (id)context
	  exception: (OFException *)e
{
	if (e != nil) {
		OFString *URL;

		[_progressBar stop];
		[_progressBar draw];
		[_progressBar release];
		_progressBar = nil;

		if (!_quiet)
			[of_stdout writeString: @"\n  Error!\n"];

		URL = [_URLs objectAtIndex: _URLIndex - 1];
		[of_stderr writeLine:
		    OF_LOCALIZED(@"download_failed_exception",
		    @"%[prog]: Failed to download <%[url]>: %[exception]",
		    @"prog", [OFApplication programName],
		    @"url", URL,
		    @"exception", e)];

		_errorCode = 1;
		goto next;
	}

	_received += length;

	[_output writeBuffer: buffer
		      length: length];

	[_progressBar setReceived: _received];

	if ([response isAtEndOfStream] ||
	    (_length >= 0 && _received >= _length)) {
		[_progressBar stop];
		[_progressBar draw];
		[_progressBar release];
		_progressBar = nil;

		if (!_quiet) {
			[of_stdout writeString: @"\n  "];
			[of_stdout writeLine:
			    OF_LOCALIZED(@"download_done", @"Done!")];
		}

		goto next;


	}

	return true;

next:






















	[self performSelector: @selector(downloadNextURL)
		   afterDelay: 0];
	return false;
}

-      (void)client: (OFHTTPClient *)client
  didReceiveHeaders: (OFDictionary OF_GENERIC(OFString *, OFString *) *)headers
	 statusCode: (int)statusCode
	    request: (OFHTTPRequest *)request
	    context: (id)context







|


<
<

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




















|
>
>



|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


<







649
650
651
652
653
654
655
656
657
658


659























660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711

712
713
714
715
716
717
718
	} else
		@throw e;

	[self performSelector: @selector(downloadNextURL)
		   afterDelay: 0];
}

-      (bool)stream: (OF_KINDOF(OFStream *))response
  didReadIntoBuffer: (void *)buffer
	     length: (size_t)length


{























	_received += length;

	[_output writeBuffer: buffer
		      length: length];

	[_progressBar setReceived: _received];

	if ([response isAtEndOfStream] ||
	    (_length >= 0 && _received >= _length)) {
		[_progressBar stop];
		[_progressBar draw];
		[_progressBar release];
		_progressBar = nil;

		if (!_quiet) {
			[of_stdout writeString: @"\n  "];
			[of_stdout writeLine:
			    OF_LOCALIZED(@"download_done", @"Done!")];
		}

		[self performSelector: @selector(downloadNextURL)
			   afterDelay: 0];
		return false;
	}

	return true;
}

-	  (void)stream: (OF_KINDOF(OFStream *))response
  didFailWithException: (id)exception
{
	OFString *URL;

	[_progressBar stop];
	[_progressBar draw];
	[_progressBar release];
	_progressBar = nil;

	if (!_quiet)
		[of_stdout writeString: @"\n  Error!\n"];

	URL = [_URLs objectAtIndex: _URLIndex - 1];
	[of_stderr writeLine: OF_LOCALIZED(
	    @"download_failed_exception",
	    @"%[prog]: Failed to download <%[url]>: %[exception]",
	    @"prog", [OFApplication programName],
	    @"url", URL,
	    @"exception", exception)];

	_errorCode = 1;
	[self performSelector: @selector(downloadNextURL)
		   afterDelay: 0];

}

-      (void)client: (OFHTTPClient *)client
  didReceiveHeaders: (OFDictionary OF_GENERIC(OFString *, OFString *) *)headers
	 statusCode: (int)statusCode
	    request: (OFHTTPRequest *)request
	    context: (id)context
863
864
865
866
867
868
869

870
871
872
873
874
875
876
877
878
879
880
881
882
883
		[_progressBar setReceived: _received];
		[_progressBar draw];
	}

	[_currentFileName release];
	_currentFileName = nil;


	[response asyncReadIntoBuffer: _buffer
			       length: [OFSystemInfo pageSize]
			       target: self
			     selector: @selector(stream:didReadIntoBuffer:
					   length:context:exception:)
			      context: nil];

	return;

next:
	[_currentFileName release];
	_currentFileName = nil;

	[self performSelector: @selector(downloadNextURL)







>

|
<
<
<
<
<







862
863
864
865
866
867
868
869
870
871





872
873
874
875
876
877
878
		[_progressBar setReceived: _received];
		[_progressBar draw];
	}

	[_currentFileName release];
	_currentFileName = nil;

	[response setDelegate: self];
	[response asyncReadIntoBuffer: _buffer
			       length: [OFSystemInfo pageSize]];





	return;

next:
	[_currentFileName release];
	_currentFileName = nil;

	[self performSelector: @selector(downloadNextURL)