ObjFW  Check-in [d2487bc7e1]

Overview
Comment:OFURL: Don't include the leading "/" in path

While Cocoa includes it, RFC 1738 explicitly states that the "/" between
host / port and path is not part of the path.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: d2487bc7e1277800c127e8601e6510d7aefaf32186a8b6e5b75b98af993b77c6
User & Date: js on 2014-07-07 00:50:01
Other Links: manifest | tags
Context
2014-07-07
22:05
Add OFUnknownXMLEntityException check-in: 6e5a920567 user: js tags: trunk
00:50
OFURL: Don't include the leading "/" in path check-in: d2487bc7e1 user: js tags: trunk
2014-07-06
11:04
OFString+*.m: Add a few missing autorelease pools check-in: b5c8b62533 user: js tags: trunk
Changes

Modified src/OFHTTPClient.m from [d38e8f0fed] to [ab4c72054a].

324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
	of_http_request_method_t method = [request method];
	OFMutableString *requestString;
	OFString *user, *password;
	OFDictionary *headers = [request headers];
	OFDataArray *entity = [request entity];
	OFTCPSocket *socket;
	OFHTTPClientResponse *response;
	OFString *line, *path, *version, *redirect, *keepAlive;
	OFMutableDictionary *serverHeaders;
	OFEnumerator *keyEnumerator, *objectEnumerator;
	OFString *key, *object;
	int status;

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







|







324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
	of_http_request_method_t method = [request method];
	OFMutableString *requestString;
	OFString *user, *password;
	OFDictionary *headers = [request headers];
	OFDataArray *entity = [request entity];
	OFTCPSocket *socket;
	OFHTTPClientResponse *response;
	OFString *line, *version, *redirect, *keepAlive;
	OFMutableDictionary *serverHeaders;
	OFEnumerator *keyEnumerator, *objectEnumerator;
	OFString *key, *object;
	int status;

	if (![scheme isEqual: @"http"] && ![scheme isEqual: @"https"])
		@throw [OFUnsupportedProtocolException exceptionWithURL: URL];
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
		}

		[_lastResponse release];
		_lastResponse = nil;
	} else
		socket = [self OF_createSocketForRequest: request];

	if ([(path = [URL path]) length] == 0)
		path = @"/";

	/*
	 * 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.
	 */
	if ([URL query] != nil)
		requestString = [OFMutableString stringWithFormat:
		    @"%s %@?%@ HTTP/%@\r\n",
		    of_http_request_method_to_string(method), path, [URL query],
		    [request protocolVersionString]];
	else
		requestString = [OFMutableString stringWithFormat:
		    @"%s %@ HTTP/%@\r\n",
		    of_http_request_method_to_string(method), path,
		    [request protocolVersionString]];

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







<
<
<







|
|
|


|
|







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
		}

		[_lastResponse release];
		_lastResponse = nil;
	} else
		socket = [self OF_createSocketForRequest: request];




	/*
	 * 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.
	 */
	if ([URL query] != nil)
		requestString = [OFMutableString stringWithFormat:
		    @"%s /%@?%@ HTTP/%@\r\n",
		    of_http_request_method_to_string(method), [URL path],
		    [URL query], [request protocolVersionString]];
	else
		requestString = [OFMutableString stringWithFormat:
		    @"%s /%@ HTTP/%@\r\n",
		    of_http_request_method_to_string(method), [URL path],
		    [request protocolVersionString]];

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

Modified src/OFHTTPServer.m from [79b7aacf76] to [3efdaebeee].

381
382
383
384
385
386
387

388
389
390
391
392
393
394

	abort();
}

- (bool)parseProlog: (OFString*)line
{
	OFString *method;

	size_t pos;

	@try {
		OFString *version = [line
		    substringWithRange: of_range([line length] - 9, 9)];
		of_unichar_t tmp;








>







381
382
383
384
385
386
387
388
389
390
391
392
393
394
395

	abort();
}

- (bool)parseProlog: (OFString*)line
{
	OFString *method;
	OFMutableString *path;
	size_t pos;

	@try {
		OFString *version = [line
		    substringWithRange: of_range([line length] - 9, 9)];
		of_unichar_t tmp;

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
		_method = of_http_request_method_from_string(
		    [method UTF8String]);
	} @catch (OFInvalidFormatException *e) {
		return [self sendErrorAndClose: 405];
	}

	@try {


		_path = [line substringWithRange:
		    of_range(pos + 1, [line length] - pos - 10)];
	} @catch (OFOutOfRangeException *e) {
		return [self sendErrorAndClose: 400];
	}

	_path = [[_path stringByDeletingEnclosingWhitespaces] retain];

	if (![_path hasPrefix: @"/"])
		return [self sendErrorAndClose: 400];




	_headers = [[OFMutableDictionary alloc] init];

	_state = PARSING_HEADERS;

	return true;
}

- (bool)parseHeaders: (OFString*)line
{







>
>
|
|



>
|

|


>
>
>

>







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
		_method = of_http_request_method_from_string(
		    [method UTF8String]);
	} @catch (OFInvalidFormatException *e) {
		return [self sendErrorAndClose: 405];
	}

	@try {
		of_range_t range = of_range(pos + 1, [line length] - pos - 10);

		path = [[[line substringWithRange:
		    range] mutableCopy] autorelease];
	} @catch (OFOutOfRangeException *e) {
		return [self sendErrorAndClose: 400];
	}

	[path deleteEnclosingWhitespaces];

	if (![path hasPrefix: @"/"])
		return [self sendErrorAndClose: 400];

	[path deleteCharactersInRange: of_range(0, 1)];
	[path makeImmutable];

	_headers = [[OFMutableDictionary alloc] init];
	_path = [path copy];
	_state = PARSING_HEADERS;

	return true;
}

- (bool)parseHeaders: (OFString*)line
{

Modified src/OFURL.m from [bcc6187243] to [2209074f5b].

154
155
156
157
158
159
160
161
162
163
164

165
166
167
168
169
170
171
			if ((tmp = strchr(UTF8String, ';')) != NULL) {
				*tmp = '\0';

				_parameters = [[OFString alloc]
				    initWithUTF8String: tmp + 1];
			}

			_path = [[OFString alloc] initWithFormat: @"/%s",
								  UTF8String];
		} else
			_path = @"";

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








|
|
<
<
>







154
155
156
157
158
159
160
161
162


163
164
165
166
167
168
169
170
			if ((tmp = strchr(UTF8String, ';')) != NULL) {
				*tmp = '\0';

				_parameters = [[OFString alloc]
				    initWithUTF8String: tmp + 1];
			}

			_path = [[OFString alloc]
			    initWithUTF8String: UTF8String];


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

213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
			*tmp = '\0';
			_parameters = [[OFString alloc]
			    initWithUTF8String: tmp + 1];
		}

		if (*UTF8String == '/')
			_path = [[OFString alloc]
			    initWithUTF8String: UTF8String];
		else {
			void *pool;
			OFString *s;

			pool = objc_autoreleasePoolPush();

			if ([URL->_path hasSuffix: @"/"])







|







212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
			*tmp = '\0';
			_parameters = [[OFString alloc]
			    initWithUTF8String: tmp + 1];
		}

		if (*UTF8String == '/')
			_path = [[OFString alloc]
			    initWithUTF8String: UTF8String + 1];
		else {
			void *pool;
			OFString *s;

			pool = objc_autoreleasePoolPush();

			if ([URL->_path hasSuffix: @"/"])
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
		[ret appendString: _host];

	if (([_scheme isEqual: @"http"] && _port != 80) ||
	    ([_scheme isEqual: @"https"] && _port != 443))
		[ret appendFormat: @":%u", _port];

	if (_path != nil)
		[ret appendString: _path];

	if (_parameters != nil)
		[ret appendFormat: @";%@", _parameters];

	if (_query != nil)
		[ret appendFormat: @"?%@", _query];








|







477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
		[ret appendString: _host];

	if (([_scheme isEqual: @"http"] && _port != 80) ||
	    ([_scheme isEqual: @"https"] && _port != 443))
		[ret appendFormat: @":%u", _port];

	if (_path != nil)
		[ret appendFormat: @"/%@", _path];

	if (_parameters != nil)
		[ret appendFormat: @";%@", _parameters];

	if (_query != nil)
		[ret appendFormat: @"?%@", _query];

Modified tests/OFURLTests.m from [3641e79d58] to [d80e8f78e3].

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
	    [[u1 scheme] isEqual: @"http"] && [[u4 scheme] isEqual: @"file"])
	TEST(@"-[user]", [[u1 user] isEqual: @"u"] && [u4 user] == nil)
	TEST(@"-[password]",
	    [[u1 password] isEqual: @"p"] && [u4 password] == nil)
	TEST(@"-[host]", [[u1 host] isEqual: @"h"] && [u4 port] == 0)
	TEST(@"-[port]", [u1 port] == 1234)
	TEST(@"-[path]",
	    [[u1 path] isEqual: @"/f"] && [[u4 path] isEqual: @"/etc/passwd"])
	TEST(@"-[parameters]",
	    [[u1 parameters] isEqual: @"p"] && [u4 parameters] == nil)
	TEST(@"-[query]", [[u1 query] isEqual: @"q"] && [u4 query] == nil)
	TEST(@"-[fragment]",
	    [[u1 fragment] isEqual: @"f"] && [u4 fragment] == nil)

	TEST(@"-[copy]", R(u4 = [[u1 copy] autorelease]))







|







62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
	    [[u1 scheme] isEqual: @"http"] && [[u4 scheme] isEqual: @"file"])
	TEST(@"-[user]", [[u1 user] isEqual: @"u"] && [u4 user] == nil)
	TEST(@"-[password]",
	    [[u1 password] isEqual: @"p"] && [u4 password] == nil)
	TEST(@"-[host]", [[u1 host] isEqual: @"h"] && [u4 port] == 0)
	TEST(@"-[port]", [u1 port] == 1234)
	TEST(@"-[path]",
	    [[u1 path] isEqual: @"f"] && [[u4 path] isEqual: @"/etc/passwd"])
	TEST(@"-[parameters]",
	    [[u1 parameters] isEqual: @"p"] && [u4 parameters] == nil)
	TEST(@"-[query]", [[u1 query] isEqual: @"q"] && [u4 query] == nil)
	TEST(@"-[fragment]",
	    [[u1 fragment] isEqual: @"f"] && [u4 fragment] == nil)

	TEST(@"-[copy]", R(u4 = [[u1 copy] autorelease]))

Modified tests/serialization.xml from [2be1e47e3a] to [d1cce18711].

1
2
3
4
5
6
7
8
9



















10
11
12
13
14
15
16
<?xml version='1.0' encoding='UTF-8'?>
<serialization xmlns='https://webkeks.org/objfw/serialization' version='1'>
  <OFMutableDictionary>
    <key>
      <OFString>Blub</OFString>
    </key>
    <object>
      <OFString>B&quot;la</OFString>
    </object>



















    <key>
      <OFList>
        <OFString>Hello</OFString>
        <OFString>Wo&#xD;ld!
How are you?</OFString>
        <OFURL>https://webkeks.org/</OFURL>
        <OFXMLElement name='x'>









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







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version='1.0' encoding='UTF-8'?>
<serialization xmlns='https://webkeks.org/objfw/serialization' version='1'>
  <OFMutableDictionary>
    <key>
      <OFString>Blub</OFString>
    </key>
    <object>
      <OFString>B&quot;la</OFString>
    </object>
    <key>
      <OFDataArray>MDEyMzQ1Njc4OTo7PEFCQ0RFRkdISklLTE1OT1BRUlNUVVZXWFla</OFDataArray>
    </key>
    <object>
      <OFString>data</OFString>
    </object>
    <key>
      <OFArray>
        <OFString>Qu&quot;xbar
test</OFString>
        <OFNumber type='signed'>1234</OFNumber>
        <OFNumber type='double'>40934a456d5cfaad</OFNumber>
        <OFMutableString>asd</OFMutableString>
        <OFDate>40934a456d5cfaad</OFDate>
      </OFArray>
    </key>
    <object>
      <OFString>Hello</OFString>
    </object>
    <key>
      <OFList>
        <OFString>Hello</OFString>
        <OFString>Wo&#xD;ld!
How are you?</OFString>
        <OFURL>https://webkeks.org/</OFURL>
        <OFXMLElement name='x'>
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
          </object>
        </OFCountedSet>
      </OFList>
    </key>
    <object>
      <OFString>list</OFString>
    </object>
    <key>
      <OFDataArray>MDEyMzQ1Njc4OTo7PEFCQ0RFRkdISklLTE1OT1BRUlNUVVZXWFla</OFDataArray>
    </key>
    <object>
      <OFString>data</OFString>
    </object>
    <key>
      <OFArray>
        <OFString>Qu&quot;xbar
test</OFString>
        <OFNumber type='signed'>1234</OFNumber>
        <OFNumber type='double'>40934a456d5cfaad</OFNumber>
        <OFMutableString>asd</OFMutableString>
        <OFDate>40934a456d5cfaad</OFDate>
      </OFArray>
    </key>
    <object>
      <OFString>Hello</OFString>
    </object>
  </OFMutableDictionary>
</serialization>







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


53
54
55
56
57
58
59



















60
61
          </object>
        </OFCountedSet>
      </OFList>
    </key>
    <object>
      <OFString>list</OFString>
    </object>



















  </OFMutableDictionary>
</serialization>