309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
|
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
|
-
+
-
+
|
{
void *pool = objc_autoreleasePoolPush();
OFURL *URL = [request URL];
OFString *scheme = [URL scheme];
of_http_request_method_t method = [request method];
OFMutableString *requestString;
OFString *user, *password;
OFDictionary *headers = [request headers];
OFMutableDictionary OF_GENERIC(OFString*, OFString*) *headers;
OFDataArray *body = [request body];
OFTCPSocket *socket;
OFHTTPClientResponse *response;
OFString *line, *version, *redirect, *connectionHeader;
bool keepAlive;
OFMutableDictionary *serverHeaders;
OFMutableDictionary OF_GENERIC(OFString*, OFString*) *serverHeaders;
OFEnumerator *keyEnumerator, *objectEnumerator;
OFString *key, *object;
int status;
if (![scheme isEqual: @"http"] && ![scheme isEqual: @"https"])
@throw [OFUnsupportedProtocolException exceptionWithURL: URL];
|
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
429
430
431
432
433
434
435
436
437
438
439
|
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
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
|
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+
+
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
+
+
-
-
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
|
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]];
headers = [[[request headers] mutableCopy] autorelease];
if (headers == nil)
headers = [OFMutableDictionary dictionary];
if (([scheme isEqual: @"http"] && [URL port] != 80) ||
([scheme isEqual: @"https"] && [URL port] != 443))
[requestString appendFormat: @"Host: %@:%d\r\n",
[URL host], [URL port]];
else
[requestString appendFormat: @"Host: %@\r\n", [URL host]];
([scheme isEqual: @"https"] && [URL port] != 443)) {
OFString *host = [OFString stringWithFormat:
@"%@:%d", [URL host], [URL port]];
[headers setObject: host
forKey: @"Host"];
} else
[headers setObject: [URL host]
forKey: @"Host"];
user = [URL user];
password = [URL password];
if ([user length] > 0 || [password length] > 0) {
OFDataArray *authorization = [OFDataArray dataArray];
OFDataArray *authorizationData = [OFDataArray dataArray];
OFString *authorization;
[authorization addItems: [user UTF8String]
count: [user UTF8StringLength]];
[authorization addItem: ":"];
[authorization addItems: [password UTF8String]
count: [password UTF8StringLength]];
[authorizationData addItems: [user UTF8String]
count: [user UTF8StringLength]];
[authorizationData addItem: ":"];
[authorizationData addItems: [password UTF8String]
count: [password UTF8StringLength]];
authorization = [OFString stringWithFormat:
@"Basic %@", [authorizationData stringByBase64Encoding]];
[requestString appendFormat:
@"Authorization: Basic %@\r\n",
[authorization stringByBase64Encoding]];
[headers setObject: authorization
forKey: @"Authorization"];
}
if ([headers objectForKey: @"User-Agent"] == nil)
[requestString appendString:
@"User-Agent: Something using ObjFW "
@"<https://webkeks.org/objfw>\r\n"];
[headers setObject: @"Something using ObjFW "
@"<https://heap.zone/objfw>"
forKey: @"User-Agent"];
if (body != nil) {
if ([headers objectForKey: @"Content-Length"] == nil)
[requestString appendFormat:
if ([headers objectForKey: @"Content-Length"] == nil) {
OFString *contentLength = [OFString stringWithFormat:
@"Content-Length: %zd\r\n",
[body itemSize] * [body count]];
@"%zd", [body itemSize] * [body count]];
[headers setObject: contentLength
forKey: @"Content-Length"];
}
if ([headers objectForKey: @"Content-Type"] == nil)
[requestString appendString:
@"Content-Type: application/x-www-form-urlencoded; "
@"charset=UTF-8\r\n"];
[headers setObject: @"application/x-www-form-"
@"urlencoded; charset=UTF-8"
forKey: @"Content-Type"];
}
if ([request protocolVersion].major == 1 &&
[request protocolVersion].minor == 0)
[headers setObject: @"keep-alive"
forKey: @"Connection"];
keyEnumerator = [headers keyEnumerator];
objectEnumerator = [headers objectEnumerator];
while ((key = [keyEnumerator nextObject]) != nil &&
(object = [objectEnumerator nextObject]) != nil)
[requestString appendFormat: @"%@: %@\r\n", key, object];
if ([request protocolVersion].major == 1 &&
[request protocolVersion].minor == 0)
[requestString appendString: @"Connection: keep-alive\r\n"];
[requestString appendString: @"\r\n"];
@try {
[socket writeString: requestString];
} @catch (OFWriteFailedException *e) {
if ([e errNo] != ECONNRESET && [e errNo] != EPIPE)
@throw e;
|