ObjFW  Check-in [326634c95d]

Overview
Comment:Implement HTTP/1.1 in OFHTTPRequest.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 326634c95dcea7cca2c54eaa5122c399ab3472273dedcf5218f9a2446d074691
User & Date: js on 2012-03-07 22:33:10
Other Links: manifest | tags
Context
2012-03-08
16:30
OFHTTPRequest: HTTP/1.1 introduces 307, handle it. check-in: 9dd8014c27 user: js tags: trunk
2012-03-07
22:33
Implement HTTP/1.1 in OFHTTPRequest. check-in: 326634c95d user: js tags: trunk
22:22
Fix a missing (auto)release. check-in: fde986d949 user: js tags: trunk
Changes

Modified src/OFHTTPRequest.m from [d53d806d31] to [b10e234803].

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#import "OFUnsupportedProtocolException.h"

#import "macros.h"

Class of_http_request_tls_socket_class = Nil;

static OF_INLINE void
normalize_key(OFString *key)
{
	uint8_t *str = (uint8_t*)[key UTF8String];
	BOOL firstLetter = YES;

	while (*str != '\0') {
		if (!isalnum(*str)) {
			firstLetter = YES;







|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#import "OFUnsupportedProtocolException.h"

#import "macros.h"

Class of_http_request_tls_socket_class = Nil;

static OF_INLINE void
normalizeKey(OFString *key)
{
	uint8_t *str = (uint8_t*)[key UTF8String];
	BOOL firstLetter = YES;

	while (*str != '\0') {
		if (!isalnum(*str)) {
			firstLetter = YES;
191
192
193
194
195
196
197

198
199
200
201
202
203
204
	OFString *line, *path;
	OFMutableDictionary *serverHeaders;
	OFDataArray *data;
	OFEnumerator *keyEnumerator, *objectEnumerator;
	OFString *key, *object, *contentLengthHeader;
	int status;
	const char *type = NULL;

	char *buffer;
	size_t bytesReceived;

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








>







191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
	OFString *line, *path;
	OFMutableDictionary *serverHeaders;
	OFDataArray *data;
	OFEnumerator *keyEnumerator, *objectEnumerator;
	OFString *key, *object, *contentLengthHeader;
	int status;
	const char *type = NULL;
	BOOL chunked;
	char *buffer;
	size_t bytesReceived;

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

233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
	if (requestType == OF_HTTP_REQUEST_TYPE_POST)
		type = "POST";

	if ([(path = [URL path]) isEqual: @""])
		path = @"/";

	if ([URL query] != nil)
		[sock writeFormat: @"%s %@?%@ HTTP/1.0\r\n",
		    type, path, [URL query]];
	else
		[sock writeFormat: @"%s %@ HTTP/1.0\r\n", type, path];

	if ([URL port] == 80)
		[sock writeFormat: @"Host: %@\r\n", [URL host]];
	else
		[sock writeFormat: @"Host: %@:%d\r\n", [URL host],
		    [URL port]];








|


|







234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
	if (requestType == OF_HTTP_REQUEST_TYPE_POST)
		type = "POST";

	if ([(path = [URL path]) isEqual: @""])
		path = @"/";

	if ([URL query] != nil)
		[sock writeFormat: @"%s %@?%@ HTTP/1.1\r\n",
		    type, path, [URL query]];
	else
		[sock writeFormat: @"%s %@ HTTP/1.1\r\n", type, path];

	if ([URL port] == 80)
		[sock writeFormat: @"Host: %@\r\n", [URL host]];
	else
		[sock writeFormat: @"Host: %@:%d\r\n", [URL host],
		    [URL port]];

271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
	/* Work around a bug in lighttpd, see above */
	[sock flushWriteBuffer];
	[sock setBuffersWrites: NO];

	if (requestType == OF_HTTP_REQUEST_TYPE_POST)
		[sock writeString: queryString];

	/*
	 * We also need to check for HTTP/1.1 since Apache always declares the
	 * reply to be HTTP/1.1.
	 */
	line = [sock readLine];
	if (![line hasPrefix: @"HTTP/1.0 "] && ![line hasPrefix: @"HTTP/1.1 "])
		@throw [OFInvalidServerReplyException exceptionWithClass: isa];

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

	serverHeaders = [OFMutableDictionary dictionary];







<
<
<
<







272
273
274
275
276
277
278




279
280
281
282
283
284
285
	/* Work around a bug in lighttpd, see above */
	[sock flushWriteBuffer];
	[sock setBuffersWrites: NO];

	if (requestType == OF_HTTP_REQUEST_TYPE_POST)
		[sock writeString: queryString];





	line = [sock readLine];
	if (![line hasPrefix: @"HTTP/1.0 "] && ![line hasPrefix: @"HTTP/1.1 "])
		@throw [OFInvalidServerReplyException exceptionWithClass: isa];

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

	serverHeaders = [OFMutableDictionary dictionary];
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310

		if ((tmp = strchr(line_c, ':')) == NULL)
			@throw [OFInvalidServerReplyException
			    exceptionWithClass: isa];

		key = [OFString stringWithUTF8String: line_c
					      length: tmp - line_c];
		normalize_key(key);

		do {
			tmp++;
		} while (*tmp == ' ');

		value = [OFString stringWithUTF8String: tmp];








|







293
294
295
296
297
298
299
300
301
302
303
304
305
306
307

		if ((tmp = strchr(line_c, ':')) == NULL)
			@throw [OFInvalidServerReplyException
			    exceptionWithClass: isa];

		key = [OFString stringWithUTF8String: line_c
					      length: tmp - line_c];
		normalizeKey(key);

		do {
			tmp++;
		} while (*tmp == ' ');

		value = [OFString stringWithUTF8String: tmp];

348
349
350
351
352
353
354


355
356
357
358











































359
360
361
362
363
364
365

366
367
368
369
370



371
372
373


374
375
376
377
378
379
380
	}

	[delegate request: self
	didReceiveHeaders: serverHeaders
	   withStatusCode: status];

	data = (storesData ? [OFDataArray dataArray] : nil);



	buffer = [self allocMemoryWithSize: of_pagesize];
	bytesReceived = 0;
	@try {











































		size_t len;

		while ((len = [sock readNBytes: of_pagesize
				    intoBuffer: buffer]) > 0) {
			[delegate request: self
			   didReceiveData: buffer
			       withLength: len];


			bytesReceived += len;
			[data addNItems: len
			     fromCArray: buffer];
		}



	} @finally {
		[self freeMemory: buffer];
	}



	if ((contentLengthHeader =
	    [serverHeaders objectForKey: @"Content-Length"]) != nil) {
		intmax_t cl = [contentLengthHeader decimalValue];

		if (cl > SIZE_MAX)
			@throw [OFOutOfRangeException exceptionWithClass: isa];







>
>




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

|
|
|
|
|
>

|
|
|
|
>
>
>



>
>







345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
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
	}

	[delegate request: self
	didReceiveHeaders: serverHeaders
	   withStatusCode: status];

	data = (storesData ? [OFDataArray dataArray] : nil);
	chunked = [[serverHeaders objectForKey: @"Transfer-Encoding"]
	    isEqual: @"chunked"];

	buffer = [self allocMemoryWithSize: of_pagesize];
	bytesReceived = 0;
	@try {
		OFAutoreleasePool *pool2 = [[OFAutoreleasePool alloc] init];

		if (chunked) {
			for (;;) {
				size_t pos, toRead;

				line = [sock readLine];

				pos = [line
				    indexOfFirstOccurrenceOfString: @";"];
				if (pos != OF_INVALID_INDEX)
					line = [line substringWithRange:
					    of_range(0, pos)];

				toRead = (size_t)[line hexadecimalValue];
				if (toRead == 0)
					break;

				while (toRead > 0) {
					size_t length = (toRead < of_pagesize
					    ? toRead : of_pagesize);

					length = [sock readNBytes: length
						       intoBuffer: buffer];

					[delegate request: self
					   didReceiveData: buffer
					       withLength: length];

					bytesReceived += length;
					[data addNItems: length
					     fromCArray: buffer];

					toRead -= length;
				}

				if (![[sock readLine] isEqual: @""])
					@throw [OFInvalidServerReplyException
					    exceptionWithClass: isa];

				[pool2 releaseObjects];
			}
		} else {
			size_t length;

			while ((length = [sock readNBytes: of_pagesize
					       intoBuffer: buffer]) > 0) {
				[delegate request: self
				   didReceiveData: buffer
				       withLength: length];
				[pool2 releaseObjects];

				bytesReceived += length;
				[data addNItems: length
				     fromCArray: buffer];
			}
		}

		[pool2 release];
	} @finally {
		[self freeMemory: buffer];
	}

	[sock close];

	if ((contentLengthHeader =
	    [serverHeaders objectForKey: @"Content-Length"]) != nil) {
		intmax_t cl = [contentLengthHeader decimalValue];

		if (cl > SIZE_MAX)
			@throw [OFOutOfRangeException exceptionWithClass: isa];

Modified tests/OFHTTPRequestTests.m from [f3f396f3a5] to [bc194dbeb7].

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
	[listener listen];

	[cond signal];
	[cond unlock];

	client = [listener accept];

	if (![[client readLine] isEqual: @"GET /foo HTTP/1.0"])
		assert(0);

	if (![[client readLine] isEqual:
	    [OFString stringWithFormat: @"Host: 127.0.0.1:%" @PRIu16, port]])
		assert(0);

	if (![[client readLine] hasPrefix: @"User-Agent:"])
		assert(0);

	if (![[client readLine] isEqual: @""])
		assert(0);

	[client writeString: @"HTTP/1.0 200 OK\r\n"
			     @"cONTeNT-lENgTH: 7\r\n"
			     @"\r\n"
			     @"foo\n"
			     @"bar"];
	[client close];

	return nil;







|












|







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
	[listener listen];

	[cond signal];
	[cond unlock];

	client = [listener accept];

	if (![[client readLine] isEqual: @"GET /foo HTTP/1.1"])
		assert(0);

	if (![[client readLine] isEqual:
	    [OFString stringWithFormat: @"Host: 127.0.0.1:%" @PRIu16, port]])
		assert(0);

	if (![[client readLine] hasPrefix: @"User-Agent:"])
		assert(0);

	if (![[client readLine] isEqual: @""])
		assert(0);

	[client writeString: @"HTTP/1.1 200 OK\r\n"
			     @"cONTeNT-lENgTH: 7\r\n"
			     @"\r\n"
			     @"foo\n"
			     @"bar"];
	[client close];

	return nil;