ObjFW  Check-in [f6d9264de0]

Overview
Comment:OFHTTPServer: Add support for TLS
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: f6d9264de00f0068a9f85e59ee3de1727295526aba3f65e2f59e8253bb017840
User & Date: js on 2018-11-10 10:01:24
Other Links: manifest | tags
Context
2018-11-10
10:57
Update buildsys check-in: c30c9fe6ad user: js tags: trunk
10:01
OFHTTPServer: Add support for TLS check-in: f6d9264de0 user: js tags: trunk
2018-11-04
20:46
ofhttp: Use OFSandbox's new unveiling check-in: c554cd71da user: js tags: trunk
Changes

Modified src/OFHTTPServer.h from [108c0e95d0] to [b239acc622].

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
 *
 * @brief A class for creating a simple HTTP server inside of applications.
 */
@interface OFHTTPServer: OFObject
{
	OFString *_Nullable _host;
	uint16_t _port;



	id <OFHTTPServerDelegate> _Nullable _delegate;
	OFString *_Nullable _name;
	OFTCPSocket *_Nullable _listeningSocket;
}

/*!
 * @brief The host on which the HTTP server will listen.
 */
@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *host;

/*!
 * @brief The port on which the HTTP server will listen.
 */
@property (nonatomic) uint16_t port;

























/*!
 * @brief The delegate for the HTTP server.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFHTTPServerDelegate> delegate;

/*!







>
>
>


|












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







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
 *
 * @brief A class for creating a simple HTTP server inside of applications.
 */
@interface OFHTTPServer: OFObject
{
	OFString *_Nullable _host;
	uint16_t _port;
	bool _usesTLS;
	OFString *_Nullable _certificateFile, *_Nullable _privateKeyFile;
	const char *_Nullable _privateKeyPassphrase;
	id <OFHTTPServerDelegate> _Nullable _delegate;
	OFString *_Nullable _name;
	OF_KINDOF(OFTCPSocket *) _Nullable _listeningSocket;
}

/*!
 * @brief The host on which the HTTP server will listen.
 */
@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *host;

/*!
 * @brief The port on which the HTTP server will listen.
 */
@property (nonatomic) uint16_t port;

/*!
 * @brief Whether the HTTP server uses TLS.
 */
@property (nonatomic) bool usesTLS;

/*!
 * @brief The path to the X.509 certificate file to use for TLS.
 */
@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *certificateFile;

/*!
 * @brief The path to the PKCS#8 private key file to use for TLS.
 */
@property OF_NULLABLE_PROPERTY (copy, nonatomic) OFString *privateKeyFile;

/*!
 * @brief The passphrase to decrypt the PKCS#8 private key file for TLS.
 *
 * @warning You have to ensure that this is in secure memory protected from
 *	    swapping! This is also the reason why this is not an OFString.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    const char *privateKeyPassphrase;

/*!
 * @brief The delegate for the HTTP server.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFHTTPServerDelegate> delegate;

/*!

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

24
25
26
27
28
29
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
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
#import "OFData.h"
#import "OFDate.h"
#import "OFDictionary.h"
#import "OFHTTPRequest.h"
#import "OFHTTPResponse.h"
#import "OFNumber.h"
#import "OFTCPSocket.h"

#import "OFTimer.h"
#import "OFURL.h"

#import "OFAlreadyConnectedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFNotOpenException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"

#import "OFWriteFailedException.h"

#import "socket_helpers.h"

#define BUFFER_SIZE 1024

/*
 * 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>
{
	OFTCPSocket *_socket;
	OFHTTPServer *_server;
	OFHTTPRequest *_request;
	bool _chunked, _headersSent;
}

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

@interface OFHTTPServer_Connection: OFObject
{
@public
	OFTCPSocket *_socket;
	OFHTTPServer *_server;
	OFTimer *_timer;
	enum {
		AWAITING_PROLOG,
		PARSING_HEADERS,
		SEND_RESPONSE
	} _state;
	uint8_t _HTTPMinorVersion;
	of_http_request_method_t _method;
	OFString *_host, *_path;
	uint16_t _port;
	OFMutableDictionary *_headers;
	size_t _contentLength;
	OFStream *_requestBody;
}

- (instancetype)initWithSocket: (OFTCPSocket *)sock
			server: (OFHTTPServer *)server;
- (bool)socket: (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>
{
	OFTCPSocket *_socket;
	uintmax_t _toRead;
	bool _atEndOfStream;
}

- (instancetype)initWithSocket: (OFTCPSocket *)sock
		 contentLength: (uintmax_t)contentLength;
@end

static const char *
statusCodeToString(short code)
{
	switch (code) {







>









>












|
|






|





|







|
















|

|











|




|







24
25
26
27
28
29
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
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
#import "OFData.h"
#import "OFDate.h"
#import "OFDictionary.h"
#import "OFHTTPRequest.h"
#import "OFHTTPResponse.h"
#import "OFNumber.h"
#import "OFTCPSocket.h"
#import "OFTLSSocket.h"
#import "OFTimer.h"
#import "OFURL.h"

#import "OFAlreadyConnectedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFNotOpenException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"
#import "OFUnsupportedProtocolException.h"
#import "OFWriteFailedException.h"

#import "socket_helpers.h"

#define BUFFER_SIZE 1024

/*
 * 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,
		PARSING_HEADERS,
		SEND_RESPONSE
	} _state;
	uint8_t _HTTPMinorVersion;
	of_http_request_method_t _method;
	OFString *_host, *_path;
	uint16_t _port;
	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>
{
	OF_KINDOF(OFTCPSocket *) _socket;
	uintmax_t _toRead;
	bool _atEndOfStream;
}

- (instancetype)initWithSocket: (OF_KINDOF(OFTCPSocket *))sock
		 contentLength: (uintmax_t)contentLength;
@end

static const char *
statusCodeToString(short code)
{
	switch (code) {
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
	}

	return [OFString stringWithUTF8StringNoCopy: cString
				       freeWhenDone: true];
}

@implementation OFHTTPServerResponse
- (instancetype)initWithSocket: (OFTCPSocket *)sock
			server: (OFHTTPServer *)server
		       request: (OFHTTPRequest *)request
{
	self = [super init];

	_statusCode = 500;
	_socket = [sock retain];







|







229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
	}

	return [OFString stringWithUTF8StringNoCopy: cString
				       freeWhenDone: true];
}

@implementation OFHTTPServerResponse
- (instancetype)initWithSocket: (OF_KINDOF(OFTCPSocket *))sock
			server: (OFHTTPServer *)server
		       request: (OFHTTPRequest *)request
{
	self = [super init];

	_statusCode = 500;
	_socket = [sock retain];
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
		return -1;

	return [_socket fileDescriptorForWriting];
}
@end

@implementation OFHTTPServer_Connection
- (instancetype)initWithSocket: (OFTCPSocket *)sock
			server: (OFHTTPServer *)server
{
	self = [super init];

	@try {
		_socket = [sock retain];
		_server = [server retain];







|







366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
		return -1;

	return [_socket fileDescriptorForWriting];
}
@end

@implementation OFHTTPServer_Connection
- (instancetype)initWithSocket: (OF_KINDOF(OFTCPSocket *))sock
			server: (OFHTTPServer *)server
{
	self = [super init];

	@try {
		_socket = [sock retain];
		_server = [server retain];
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
	[_path release];
	[_headers release];
	[_requestBody release];

	[super dealloc];
}

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








|







405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
	[_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;

655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
			  response: response];

	objc_autoreleasePoolPop(pool);
}
@end

@implementation OFHTTPServerRequestBodyStream
- (instancetype)initWithSocket: (OFTCPSocket *)sock
		     contentLength: (uintmax_t)contentLength
{
	self = [super init];

	@try {
		_socket = [sock retain];
		_toRead = contentLength;
	} @catch (id e) {







|
|







657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
			  response: response];

	objc_autoreleasePoolPop(pool);
}
@end

@implementation OFHTTPServerRequestBodyStream
- (instancetype)initWithSocket: (OF_KINDOF(OFTCPSocket *))sock
		 contentLength: (uintmax_t)contentLength
{
	self = [super init];

	@try {
		_socket = [sock retain];
		_toRead = contentLength;
	} @catch (id e) {
722
723
724
725
726
727
728
729




730
731
732
733
734
735
736
{
	[_socket release];
	_socket = nil;
}
@end

@implementation OFHTTPServer
@synthesize host = _host, port = _port, delegate = _delegate, name = _name;





+ (instancetype)server
{
	return [[[self alloc] init] autorelease];
}

- (instancetype)init







|
>
>
>
>







724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
{
	[_socket release];
	_socket = nil;
}
@end

@implementation OFHTTPServer
@synthesize host = _host, port = _port, usesTLS = _usesTLS;
@synthesize certificateFile = _certificateFile;
@synthesize privateKeyFile = _privateKeyFile;
@synthesize privateKeyPassphrase = _privateKeyPassphrase, delegate = _delegate;
@synthesize name = _name;

+ (instancetype)server
{
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
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
{
	if (_host == nil)
		@throw [OFInvalidArgumentException exception];

	if (_listeningSocket != nil)
		@throw [OFAlreadyConnectedException exception];















	_listeningSocket = [[OFTCPSocket alloc] init];

	_port = [_listeningSocket bindToHost: _host
					port: _port];
	[_listeningSocket listen];

	[_listeningSocket asyncAcceptWithTarget: self
				       selector: @selector(of_socket:
						     didAcceptSocket:context:
						     exception:)
					context: nil];
}

- (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:







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


















|
|







762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
{
	if (_host == nil)
		@throw [OFInvalidArgumentException exception];

	if (_listeningSocket != nil)
		@throw [OFAlreadyConnectedException exception];

	if (_usesTLS) {
		id <OFTLSSocket> listeningSocket;

		if (of_tls_socket_class == Nil)
			@throw [OFUnsupportedProtocolException exception];

		_listeningSocket = [[of_tls_socket_class alloc] init];

		listeningSocket = _listeningSocket;
		[listeningSocket setCertificateFile: _certificateFile];
		[listeningSocket setPrivateKeyFile: _privateKeyFile];
		[listeningSocket
		    setPrivateKeyPassphrase: _privateKeyPassphrase];
	} else
		_listeningSocket = [[OFTCPSocket alloc] init];

	_port = [_listeningSocket bindToHost: _host
					port: _port];
	[_listeningSocket listen];

	[_listeningSocket asyncAcceptWithTarget: self
				       selector: @selector(of_socket:
						     didAcceptSocket:context:
						     exception:)
					context: nil];
}

- (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:

Modified src/exceptions/OFUnsupportedProtocolException.h from [37fe44c75d] to [f460fb7900].

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
}

/*!
 * @brief The URL whose protocol is unsupported.
 */
@property (readonly, nonatomic) OFURL *URL;

+ (instancetype)exception OF_UNAVAILABLE;

/*!
 * @brief Creates a new, autoreleased unsupported protocol exception.
 *
 * @param URL The URL whose protocol is unsupported
 * @return A new, autoreleased unsupported protocol exception
 */
+ (instancetype)exceptionWithURL: (OFURL*)URL;

- (instancetype)init OF_UNAVAILABLE;

/*!
 * @brief Initializes an already allocated unsupported protocol exception
 *
 * @param URL The URL whose protocol is unsupported
 * @return An initialized unsupported protocol exception
 */
- (instancetype)initWithURL: (OFURL*)URL OF_DESIGNATED_INITIALIZER;
@end

OF_ASSUME_NONNULL_END







<
<








<
<










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
}

/*!
 * @brief The URL whose protocol is unsupported.
 */
@property (readonly, nonatomic) OFURL *URL;



/*!
 * @brief Creates a new, autoreleased unsupported protocol exception.
 *
 * @param URL The URL whose protocol is unsupported
 * @return A new, autoreleased unsupported protocol exception
 */
+ (instancetype)exceptionWithURL: (OFURL*)URL;



/*!
 * @brief Initializes an already allocated unsupported protocol exception
 *
 * @param URL The URL whose protocol is unsupported
 * @return An initialized unsupported protocol exception
 */
- (instancetype)initWithURL: (OFURL*)URL OF_DESIGNATED_INITIALIZER;
@end

OF_ASSUME_NONNULL_END

Modified src/exceptions/OFUnsupportedProtocolException.m from [06fe181f28] to [8dbcda3957].

20
21
22
23
24
25
26
27
28
29
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
#import "OFUnsupportedProtocolException.h"
#import "OFString.h"
#import "OFURL.h"

@implementation OFUnsupportedProtocolException
@synthesize URL = _URL;

+ (instancetype)exception
{
	OF_UNRECOGNIZED_SELECTOR
}

+ (instancetype)exceptionWithURL: (OFURL *)URL
{
	return [[[self alloc] initWithURL: URL] autorelease];
}

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

- (instancetype)initWithURL: (OFURL *)URL
{
	self = [super init];

	_URL = [URL retain];

	return self;
}

- (void)dealloc
{
	[_URL release];

	[super dealloc];
}

- (OFString *)description
{

	return [OFString stringWithFormat:
	    @"The protocol of URL %@ is not supported!", _URL];


}
@end







<
<
<
<
<





<
<
<
<
<


















>
|
|
>
>


20
21
22
23
24
25
26





27
28
29
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
#import "OFUnsupportedProtocolException.h"
#import "OFString.h"
#import "OFURL.h"

@implementation OFUnsupportedProtocolException
@synthesize URL = _URL;






+ (instancetype)exceptionWithURL: (OFURL *)URL
{
	return [[[self alloc] initWithURL: URL] autorelease];
}






- (instancetype)initWithURL: (OFURL *)URL
{
	self = [super init];

	_URL = [URL retain];

	return self;
}

- (void)dealloc
{
	[_URL release];

	[super dealloc];
}

- (OFString *)description
{
	if (_URL != nil)
		return [OFString stringWithFormat:
		    @"The protocol of URL %@ is not supported!", _URL];
	else
		return @"The requested protocol is unsupported!";
}
@end