|
@@ -622,45 +622,56 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
|
|
|
return success;
|
|
|
}
|
|
|
|
|
|
-static CFHTTPMessageRef _CreateHTTPMessageFromFileDump(NSString* path, BOOL isRequest) {
|
|
|
- NSData* data = [NSData dataWithContentsOfFile:path];
|
|
|
- if (data) {
|
|
|
- CFHTTPMessageRef message = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, isRequest);
|
|
|
- if (CFHTTPMessageAppendBytes(message, data.bytes, data.length)) {
|
|
|
- return message;
|
|
|
- }
|
|
|
- CFRelease(message);
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
|
|
+
|
|
|
+static CFHTTPMessageRef _CreateHTTPMessageFromData(NSData* data, BOOL isRequest) {
|
|
|
+ CFHTTPMessageRef message = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, isRequest);
|
|
|
+ if (CFHTTPMessageAppendBytes(message, data.bytes, data.length)) {
|
|
|
+ return message;
|
|
|
}
|
|
|
+ CFRelease(message);
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-static CFHTTPMessageRef _CreateHTTPMessageFromHTTPRequestResponse(CFHTTPMessageRef request) {
|
|
|
+static CFHTTPMessageRef _CreateHTTPMessageFromPerformingRequest(NSData* inData, NSUInteger port) {
|
|
|
CFHTTPMessageRef response = NULL;
|
|
|
- CFReadStreamRef stream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request);
|
|
|
- if (CFReadStreamOpen(stream)) {
|
|
|
- CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 0);
|
|
|
- CFDataSetLength(data, 256 * 1024);
|
|
|
- CFIndex length = 0;
|
|
|
- while (1) {
|
|
|
- CFIndex result = CFReadStreamRead(stream, CFDataGetMutableBytePtr(data) + length, CFDataGetLength(data) - length);
|
|
|
- if (result <= 0) {
|
|
|
- break;
|
|
|
- }
|
|
|
- length += result;
|
|
|
- if (length >= CFDataGetLength(data)) {
|
|
|
- CFDataSetLength(data, 2 * CFDataGetLength(data));
|
|
|
- }
|
|
|
- }
|
|
|
- if (CFReadStreamGetStatus(stream) == kCFStreamStatusAtEnd) {
|
|
|
- response = (CFHTTPMessageRef)CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader);
|
|
|
- if (response) {
|
|
|
- CFDataSetLength(data, length);
|
|
|
- CFHTTPMessageSetBody(response, data);
|
|
|
+ int httpSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
|
+ if (httpSocket > 0) {
|
|
|
+ struct sockaddr_in addr4;
|
|
|
+ bzero(&addr4, sizeof(addr4));
|
|
|
+ addr4.sin_len = sizeof(port);
|
|
|
+ addr4.sin_family = AF_INET;
|
|
|
+ addr4.sin_port = htons(8080);
|
|
|
+ addr4.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
+ if (connect(httpSocket, (void*)&addr4, sizeof(addr4)) == 0) {
|
|
|
+ if (write(httpSocket, inData.bytes, inData.length) == (ssize_t)inData.length) {
|
|
|
+ NSMutableData* outData = [[NSMutableData alloc] initWithLength:(256 * 1024)];
|
|
|
+ NSUInteger length = 0;
|
|
|
+ while (1) {
|
|
|
+ ssize_t result = read(httpSocket, (char*)outData.mutableBytes + length, outData.length - length);
|
|
|
+ if (result < 0) {
|
|
|
+ length = NSNotFound;
|
|
|
+ break;
|
|
|
+ } else if (result == 0) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ length += result;
|
|
|
+ if (length >= outData.length) {
|
|
|
+ outData.length = 2 * outData.length;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (length != NSNotFound) {
|
|
|
+ outData.length = length;
|
|
|
+ response = _CreateHTTPMessageFromData(outData, NO);
|
|
|
+ } else {
|
|
|
+ DNOT_REACHED();
|
|
|
+ }
|
|
|
+ ARC_RELEASE(outData);
|
|
|
}
|
|
|
}
|
|
|
- CFRelease(data);
|
|
|
- CFReadStreamClose(stream);
|
|
|
- CFRelease(stream);
|
|
|
+ close(httpSocket);
|
|
|
}
|
|
|
return response;
|
|
|
}
|
|
@@ -675,6 +686,7 @@ static void _LogResult(NSString* format, ...) {
|
|
|
}
|
|
|
|
|
|
- (NSInteger)runTestsInDirectory:(NSString*)path withPort:(NSUInteger)port {
|
|
|
+ NSArray* ignoredHeaders = @[@"Date", @"Etag"]; // Dates are always different by definition and ETags depend on file system node IDs
|
|
|
NSInteger result = -1;
|
|
|
if ([self startWithPort:port bonjourName:nil]) {
|
|
|
|
|
@@ -687,72 +699,85 @@ static void _LogResult(NSString* format, ...) {
|
|
|
@autoreleasepool {
|
|
|
NSString* index = [[requestFile componentsSeparatedByString:@"-"] firstObject];
|
|
|
BOOL success = NO;
|
|
|
- CFHTTPMessageRef request = _CreateHTTPMessageFromFileDump([path stringByAppendingPathComponent:requestFile], YES);
|
|
|
- if (request) {
|
|
|
- _LogResult(@"[%i] %@ %@", (int)[index integerValue], ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestMethod(request)), [ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestURL(request)) path]);
|
|
|
- NSString* prefix = [index stringByAppendingString:@"-"];
|
|
|
- for (NSString* responseFile in files) {
|
|
|
- if ([responseFile hasPrefix:prefix] && [responseFile hasSuffix:@".response"]) {
|
|
|
- CFHTTPMessageRef expectedResponse = _CreateHTTPMessageFromFileDump([path stringByAppendingPathComponent:responseFile], NO);
|
|
|
- if (expectedResponse) {
|
|
|
- CFHTTPMessageRef actualResponse = _CreateHTTPMessageFromHTTPRequestResponse(request);
|
|
|
- if (actualResponse) {
|
|
|
- success = YES;
|
|
|
-
|
|
|
- CFIndex expectedStatusCode = CFHTTPMessageGetResponseStatusCode(expectedResponse);
|
|
|
- CFIndex actualStatusCode = CFHTTPMessageGetResponseStatusCode(actualResponse);
|
|
|
- if (actualStatusCode != expectedStatusCode) {
|
|
|
- _LogResult(@" Status code not matching:\n Expected: %i\n Actual: %i", (int)expectedStatusCode, (int)actualStatusCode);
|
|
|
- success = NO;
|
|
|
- }
|
|
|
-
|
|
|
- NSDictionary* expectedHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(expectedResponse));
|
|
|
- NSDictionary* actualHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(actualResponse));
|
|
|
- for (NSString* expectedHeader in expectedHeaders) {
|
|
|
- if ([expectedHeader isEqualToString:@"Date"]) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- NSString* expectedValue = [expectedHeaders objectForKey:expectedHeader];
|
|
|
- NSString* actualValue = [actualHeaders objectForKey:expectedHeader];
|
|
|
- if (![actualValue isEqualToString:expectedValue]) {
|
|
|
- _LogResult(@" Header '%@' not matching:\n Expected: \"%@\"\n Actual: \"%@\"", expectedHeader, expectedValue, actualValue);
|
|
|
- success = NO;
|
|
|
- }
|
|
|
- }
|
|
|
- for (NSString* actualHeader in actualHeaders) {
|
|
|
- if (![expectedHeaders objectForKey:actualHeader]) {
|
|
|
- _LogResult(@" Header '%@' not matching:\n Expected: \"%@\"\n Actual: \"%@\"", actualHeader, nil, [actualHeaders objectForKey:actualHeader]);
|
|
|
- success = NO;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- NSData* expectedBody = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyBody(expectedResponse));
|
|
|
- NSData* actualBody = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyBody(actualResponse));
|
|
|
- if (![actualBody isEqualToData:expectedBody]) {
|
|
|
- _LogResult(@" Bodies not matching:\n Expected: %lu bytes\n Actual: %lu bytes", (unsigned long)expectedBody.length, (unsigned long)actualBody.length);
|
|
|
- success = NO;
|
|
|
-
|
|
|
- if (_IsTextContentType([expectedHeaders objectForKey:@"Content-Type"])) {
|
|
|
- NSString* expectedPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
|
|
|
- NSString* actualPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
|
|
|
- if ([expectedBody writeToFile:expectedPath atomically:YES] && [actualBody writeToFile:actualPath atomically:YES]) {
|
|
|
- NSTask* task = [[NSTask alloc] init];
|
|
|
- [task setLaunchPath:@"/usr/bin/opendiff"];
|
|
|
- [task setArguments:@[expectedPath, actualPath]];
|
|
|
- [task launch];
|
|
|
- ARC_RELEASE(task);
|
|
|
+ NSData* requestData = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:requestFile]];
|
|
|
+ if (requestData) {
|
|
|
+ CFHTTPMessageRef request = _CreateHTTPMessageFromData(requestData, YES);
|
|
|
+ if (request) {
|
|
|
+ NSString* requestMethod = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestMethod(request));
|
|
|
+ NSURL* requestURL = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestURL(request));
|
|
|
+ _LogResult(@"[%i] %@ %@", (int)[index integerValue], requestMethod, requestURL.path);
|
|
|
+ NSString* prefix = [index stringByAppendingString:@"-"];
|
|
|
+ for (NSString* responseFile in files) {
|
|
|
+ if ([responseFile hasPrefix:prefix] && [responseFile hasSuffix:@".response"]) {
|
|
|
+ NSData* responseData = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:responseFile]];
|
|
|
+ if (responseData) {
|
|
|
+ CFHTTPMessageRef expectedResponse = _CreateHTTPMessageFromData(responseData, NO);
|
|
|
+ if (expectedResponse) {
|
|
|
+ CFHTTPMessageRef actualResponse = _CreateHTTPMessageFromPerformingRequest(requestData, port);
|
|
|
+ if (actualResponse) {
|
|
|
+ success = YES;
|
|
|
+
|
|
|
+ CFIndex expectedStatusCode = CFHTTPMessageGetResponseStatusCode(expectedResponse);
|
|
|
+ CFIndex actualStatusCode = CFHTTPMessageGetResponseStatusCode(actualResponse);
|
|
|
+ if (actualStatusCode != expectedStatusCode) {
|
|
|
+ _LogResult(@" Status code not matching:\n Expected: %i\n Actual: %i", (int)expectedStatusCode, (int)actualStatusCode);
|
|
|
+ success = NO;
|
|
|
+ }
|
|
|
+
|
|
|
+ NSDictionary* expectedHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(expectedResponse));
|
|
|
+ NSDictionary* actualHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(actualResponse));
|
|
|
+ for (NSString* expectedHeader in expectedHeaders) {
|
|
|
+ if ([ignoredHeaders containsObject:expectedHeader]) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ NSString* expectedValue = [expectedHeaders objectForKey:expectedHeader];
|
|
|
+ NSString* actualValue = [actualHeaders objectForKey:expectedHeader];
|
|
|
+ if (![actualValue isEqualToString:expectedValue]) {
|
|
|
+ _LogResult(@" Header '%@' not matching:\n Expected: \"%@\"\n Actual: \"%@\"", expectedHeader, expectedValue, actualValue);
|
|
|
+ success = NO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (NSString* actualHeader in actualHeaders) {
|
|
|
+ if (![expectedHeaders objectForKey:actualHeader]) {
|
|
|
+ _LogResult(@" Header '%@' not matching:\n Expected: \"%@\"\n Actual: \"%@\"", actualHeader, nil, [actualHeaders objectForKey:actualHeader]);
|
|
|
+ success = NO;
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ NSData* expectedBody = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyBody(expectedResponse));
|
|
|
+ NSData* actualBody = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyBody(actualResponse));
|
|
|
+ if (![actualBody isEqualToData:expectedBody]) {
|
|
|
+ _LogResult(@" Bodies not matching:\n Expected: %lu bytes\n Actual: %lu bytes", (unsigned long)expectedBody.length, (unsigned long)actualBody.length);
|
|
|
+ success = NO;
|
|
|
+#ifndef NDEBUG
|
|
|
+ if (_IsTextContentType([expectedHeaders objectForKey:@"Content-Type"])) {
|
|
|
+ NSString* expectedPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
|
|
|
+ NSString* actualPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
|
|
|
+ if ([expectedBody writeToFile:expectedPath atomically:YES] && [actualBody writeToFile:actualPath atomically:YES]) {
|
|
|
+ NSTask* task = [[NSTask alloc] init];
|
|
|
+ [task setLaunchPath:@"/usr/bin/opendiff"];
|
|
|
+ [task setArguments:@[expectedPath, actualPath]];
|
|
|
+ [task launch];
|
|
|
+ ARC_RELEASE(task);
|
|
|
+ }
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ CFRelease(actualResponse);
|
|
|
}
|
|
|
+ CFRelease(expectedResponse);
|
|
|
}
|
|
|
-
|
|
|
- CFRelease(actualResponse);
|
|
|
+ } else {
|
|
|
+ DNOT_REACHED();
|
|
|
}
|
|
|
- CFRelease(expectedResponse);
|
|
|
+ break;
|
|
|
}
|
|
|
- break;
|
|
|
}
|
|
|
+ CFRelease(request);
|
|
|
}
|
|
|
- CFRelease(request);
|
|
|
+ } else {
|
|
|
+ DNOT_REACHED();
|
|
|
}
|
|
|
_LogResult(@"");
|
|
|
if (!success) {
|