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
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)
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
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
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.0\r\n",
		[sock writeFormat: @"%s %@?%@ HTTP/1.1\r\n",
		    type, path, [URL query]];
	else
		[sock writeFormat: @"%s %@ HTTP/1.0\r\n", type, path];
		[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
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];

	/*
	 * 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];
296
297
298
299
300
301
302
303

304
305
306
307
308
309
310
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];
		normalize_key(key);
		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
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 len;
			size_t length;

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

			bytesReceived += len;
			[data addNItems: len
			     fromCArray: buffer];
		}
				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
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"])
	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.0 200 OK\r\n"
	[client writeString: @"HTTP/1.1 200 OK\r\n"
			     @"cONTeNT-lENgTH: 7\r\n"
			     @"\r\n"
			     @"foo\n"
			     @"bar"];
	[client close];

	return nil;