diff --git a/CHANGELOG.md b/CHANGELOG.md index 48377a3e..dfad2642 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,15 @@ ## [Future release] * Enabled application extension API only. [@lightsprint09](https://github.com/lightsprint09) -* Disabled a flaky redirect test and adding the known issue with redirects to the README. +* Disabled a flaky redirect test and adding the known issue with redirects to the README. [@jeffctown](https://github.com/jeffctown) [#301](https://github.com/AliSoftware/OHHTTPStubs/pull/301) * Added `isMethodHEAD()` to the `Swift` helpers. [@Simon-Kaz](https://github.com/Simon-Kaz) [#294](https://github.com/AliSoftware/OHHTTPStubs/pull/294) +* Fixed issue with not preserving correct headers when following 3xx + redirects. + [@sberrevoets](https://github.com/sberrevoets) ## [6.1.0](https://github.com/AliSoftware/OHHTTPStubs/releases/tag/6.1.0) diff --git a/OHHTTPStubs/Sources/OHHTTPStubs.m b/OHHTTPStubs/Sources/OHHTTPStubs.m index ff21e64f..3a0eaf8f 100644 --- a/OHHTTPStubs/Sources/OHHTTPStubs.m +++ b/OHHTTPStubs/Sources/OHHTTPStubs.m @@ -360,12 +360,30 @@ - (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLRespons + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { - return request; + return request; } - (NSCachedURLResponse *)cachedResponse { - return nil; + return nil; +} + +/** Drop certain headers in accordance with + * https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1411532-httpadditionalheaders + */ +- (NSMutableURLRequest *)clearAuthHeadersForRequest:(NSMutableURLRequest *)request { + NSArray* authHeadersToRemove = @[ + @"Authorization", + @"Connection", + @"Host", + @"Proxy-Authenticate", + @"Proxy-Authorization", + @"WWW-Authenticate" + ]; + for (NSString* header in authHeadersToRemove) { + [request setValue:nil forHTTPHeaderField:header]; + } + return request; } - (void)startLoading @@ -442,13 +460,16 @@ - (void)startLoading case 301: case 302: case 307: - case 308: + case 308: { //Preserve the original request method and body, and set the new location URL mReq = [self.request mutableCopy]; [mReq setURL:redirectLocationURL]; + + mReq = [self clearAuthHeadersForRequest:mReq]; + redirectRequest = (NSURLRequest*)[mReq copy]; break; - + } default: redirectRequest = [NSURLRequest requestWithURL:redirectLocationURL]; break; diff --git a/OHHTTPStubs/UnitTests/Test Suites/NSURLSessionTests.m b/OHHTTPStubs/UnitTests/Test Suites/NSURLSessionTests.m index cc84b104..6ac3e93f 100644 --- a/OHHTTPStubs/UnitTests/Test Suites/NSURLSessionTests.m +++ b/OHHTTPStubs/UnitTests/Test Suites/NSURLSessionTests.m @@ -105,10 +105,11 @@ - (void)_test_NSURLSession:(NSURLSession*)session - (void)_test_redirect_NSURLSession:(NSURLSession*)session httpMethod:(NSString *)requestHTTPMethod + headers:(NSDictionary *)headers jsonBody:(NSDictionary*)json delays:(NSTimeInterval)delay redirectStatusCode:(int)redirectStatusCode - completion:(void(^)(NSString* redirectedRequestMethod, id redirectedRequestJSONBody, NSHTTPURLResponse *redirectHTTPResponse, id finalJSONResponse, NSError *errorResponse))completion + completion:(void(^)(NSString* redirectedRequestMethod, NSDictionary * redirectedRequestHeaders, id redirectedRequestJSONBody, NSHTTPURLResponse *redirectHTTPResponse, id finalJSONResponse, NSError *errorResponse))completion { if ([NSURLSession class]) { @@ -116,6 +117,7 @@ - (void)_test_redirect_NSURLSession:(NSURLSession*)session const NSTimeInterval responseTime = delay; __block __strong NSString* capturedRedirectedRequestMethod = nil; + __block __strong NSDictionary* capturedRedirectedRequestHeaders = nil; __block __strong id capturedRedirectedRequestJSONBody = nil; __block __strong NSHTTPURLResponse* capturedRedirectHTTPResponse = nil; __block __strong id capturedResponseJSONBody = nil; @@ -139,6 +141,7 @@ - (void)_test_redirect_NSURLSession:(NSURLSession*)session return [[[request URL] path] isEqualToString:@"/newlocation"]; } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *redirectedRequest) { capturedRedirectedRequestMethod = redirectedRequest.HTTPMethod; + capturedRedirectedRequestHeaders = redirectedRequest.allHTTPHeaderFields; if (redirectedRequest.OHHTTPStubs_HTTPBody) { capturedRedirectedRequestJSONBody = [NSJSONSerialization JSONObjectWithData:redirectedRequest.OHHTTPStubs_HTTPBody options:0 error:NULL]; } else { @@ -155,6 +158,7 @@ - (void)_test_redirect_NSURLSession:(NSURLSession*)session // Building the initial request. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"foo://unknownhost:666/oldlocation"]]; request.HTTPMethod = requestHTTPMethod; + request.allHTTPHeaderFields = headers; if (requestBody) { request.HTTPBody = requestBody; @@ -193,7 +197,7 @@ - (void)_test_redirect_NSURLSession:(NSURLSession*)session [task resume]; [self waitForExpectationsWithTimeout:(requestTime+responseTime)*2+0.1 handler:nil]; - completion(capturedRedirectedRequestMethod, capturedRedirectedRequestJSONBody, + completion(capturedRedirectedRequestMethod, capturedRedirectedRequestHeaders, capturedRedirectedRequestJSONBody, capturedRedirectHTTPResponse, capturedResponseJSONBody, capturedResponseError); } @@ -214,8 +218,8 @@ - (void)test_SharedNSURLSession XCTAssertEqualObjects(jsonResponse, json, @"Unexpected data received"); }]; - [self _test_redirect_NSURLSession:session httpMethod:@"GET" jsonBody:nil delays:0.1 redirectStatusCode:301 - completion:^(NSString *redirectedRequestMethod, id redirectedRequestJSONBody, NSHTTPURLResponse *redirectHTTPResponse, id finalJSONResponse, NSError *errorResponse) + [self _test_redirect_NSURLSession:session httpMethod:@"GET" headers:nil jsonBody:nil delays:0.1 redirectStatusCode:301 + completion:^(NSString *redirectedRequestMethod, NSDictionary *redirectedRequestHeaders, id redirectedRequestJSONBody, NSHTTPURLResponse *redirectHTTPResponse, id finalJSONResponse, NSError *errorResponse) { XCTAssertEqualObjects(redirectedRequestMethod, @"GET", @"Expected redirected request to use GET method"); XCTAssertNil(redirectedRequestJSONBody, @"Expected redirected request to have empty body"); @@ -245,8 +249,8 @@ - (void)test_NSURLSessionDefaultConfig XCTAssertEqualObjects(jsonResponse, json, @"Unexpected data received"); }]; - [self _test_redirect_NSURLSession:session httpMethod:@"GET" jsonBody:nil delays:0.1 redirectStatusCode:301 - completion:^(NSString *redirectedRequestMethod, id redirectedRequestJSONBody, NSHTTPURLResponse *redirectHTTPResponse, id finalJSONResponse, NSError *errorResponse) + [self _test_redirect_NSURLSession:session httpMethod:@"GET" headers:nil jsonBody:nil delays:0.1 redirectStatusCode:301 + completion:^(NSString *redirectedRequestMethod, NSDictionary *redirectedRequestHeaders, id redirectedRequestJSONBody, NSHTTPURLResponse *redirectHTTPResponse, id finalJSONResponse, NSError *errorResponse) { XCTAssertEqualObjects(redirectedRequestMethod, @"GET", @"Expected redirected request to use GET method"); XCTAssertNil(redirectedRequestJSONBody, @"Expected redirected request to have empty body"); @@ -272,8 +276,8 @@ - (void)test_NSURLSessionDefaultConfig_notFollowingRedirects NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:delegate delegateQueue:nil]; - [self _test_redirect_NSURLSession:session httpMethod:@"GET" jsonBody:nil delays:0.1 redirectStatusCode:301 - completion:^(NSString *redirectedRequestMethod, id redirectedRequestJSONBody, NSHTTPURLResponse *redirectHTTPResponse, id finalJSONResponse, NSError *errorResponse) + [self _test_redirect_NSURLSession:session httpMethod:@"GET" headers:nil jsonBody:nil delays:0.1 redirectStatusCode:301 + completion:^(NSString *redirectedRequestMethod, NSDictionary *redirectedRequestHeaders, id redirectedRequestJSONBody, NSHTTPURLResponse *redirectHTTPResponse, id finalJSONResponse, NSError *errorResponse) { XCTAssertNil(redirectedRequestMethod, @"Expected no redirected request to fire"); XCTAssertNil(redirectedRequestJSONBody, @"Expected no redirected request to fire"); @@ -313,8 +317,8 @@ - (void)test_NSURLSessionDefaultConfig_MethodAndDataRetentionOnRedirect NSURLSessionTestDelegate* delegate = [NSURLSessionTestDelegate delegateFollowingRedirects:YES fulfillOnCompletion:nil]; NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:delegate delegateQueue:nil]; - [self _test_redirect_NSURLSession:session httpMethod:method jsonBody:json delays:0.0 redirectStatusCode:statusCode - completion:^(NSString *redirectedRequestMethod, id redirectedRequestJSONBody, NSHTTPURLResponse *redirectHTTPResponse, id finalJSONResponse, NSError *errorResponse) + [self _test_redirect_NSURLSession:session httpMethod:method headers:nil jsonBody:json delays:0.0 redirectStatusCode:statusCode + completion:^(NSString *redirectedRequestMethod, NSDictionary *redirectedRequestHeaders, id redirectedRequestJSONBody, NSHTTPURLResponse *redirectHTTPResponse, id finalJSONResponse, NSError *errorResponse) { XCTAssertEqualObjects(redirectedRequestMethod, method, @"Expected the HTTP method to be unchanged after %d redirect", statusCode); @@ -338,8 +342,8 @@ - (void)test_NSURLSessionDefaultConfig_MethodAndDataRetentionOnRedirect NSURLSessionTestDelegate* delegate = [NSURLSessionTestDelegate delegateFollowingRedirects:YES fulfillOnCompletion:nil]; NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:delegate delegateQueue:nil]; - [self _test_redirect_NSURLSession:session httpMethod:method jsonBody:json delays:0.0 redirectStatusCode:303 - completion:^(NSString *redirectedRequestMethod, id redirectedRequestJSONBody, NSHTTPURLResponse *redirectHTTPResponse, id finalJSONResponse, NSError *errorResponse) + [self _test_redirect_NSURLSession:session httpMethod:method headers:nil jsonBody:json delays:0.0 redirectStatusCode:303 + completion:^(NSString *redirectedRequestMethod, NSDictionary *redirectedRequestHeaders, id redirectedRequestJSONBody, NSHTTPURLResponse *redirectHTTPResponse, id finalJSONResponse, NSError *errorResponse) { XCTAssertEqualObjects(redirectedRequestMethod, @"GET", @"Expected 303 redirected request HTTP method to be reset to GET"); XCTAssertNil(redirectedRequestJSONBody, @"Expected 303-redirected request to have empty body"); @@ -356,6 +360,53 @@ - (void)test_NSURLSessionDefaultConfig_MethodAndDataRetentionOnRedirect NSLog(@"/!\\ Test skipped because the NSURLSession class is not available on this OS version. Run the tests a target with a more recent OS.\n"); } } + +- (void)test_NSURLSessionDefaultConfig_HeaderRetentionPolicyOnRedirect { + if ([NSURLSessionConfiguration class] && [NSURLSession class]) + { + NSArray* allMethods = @[@"GET", @"HEAD", @"POST", @"PATCH", @"PUT"]; + + /** 301, 302, 307, 308: GET, HEAD, POST, PATCH, PUT should all maintain most HTTP headers unchanged **/ + for (NSNumber* redirectStatusCode in @[@301, @302, @307, @308]) { + int statusCode = redirectStatusCode.intValue; + for (NSString* method in allMethods) { + + NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSessionTestDelegate* delegate = [NSURLSessionTestDelegate delegateFollowingRedirects:YES fulfillOnCompletion:nil]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:delegate delegateQueue:nil]; + + NSDictionary *headers = @{ + @"Authorization": @"authorization", + @"Connection": @"connection", + @"Preserved1": @"preserved" + @"Host": @"host", + @"Proxy-Authenticate": @"proxy-authenticate", + @"Proxy-Authorization": @"proxy-authorization", + @"Preserved2": @"preserved", + @"WWW-Authenticate": @"www-authenticate", + }; + [self _test_redirect_NSURLSession:session httpMethod:method headers:headers jsonBody:nil delays:0.0 redirectStatusCode:statusCode + completion:^(NSString *redirectedRequestMethod, NSDictionary *redirectedRequestHeaders, id redirectedRequestJSONBody, NSHTTPURLResponse *redirectHTTPResponse, id finalJSONResponse, NSError *errorResponse) + { + XCTAssertNil(redirectedRequestHeaders[@"Authorization"], @"Authorization header is preserved when following redirects"); + XCTAssertNil(redirectedRequestHeaders[@"Connection"], @"Connection header is preserved when following redirects"); + XCTAssertNil(redirectedRequestHeaders[@"Host"], @"Host header is preserved when following redirects"); + XCTAssertNil(redirectedRequestHeaders[@"Proxy-Authenticate"], @"Proxy-Authenticate header is preserved when following redirects"); + XCTAssertNil(redirectedRequestHeaders[@"Proxy-Authorization"], @"Proxy-Authorization header is preserved when following redirects"); + XCTAssertNil(redirectedRequestHeaders[@"WWW-Authenticate"], @"WWW-Authenticate header is preserved when following redirects"); + XCTAssertEqual(redirectedRequestHeaders[@"Preserved1"], @"preserved", @"Regular header is not preserved when following redirects"); + XCTAssertEqual(redirectedRequestHeaders[@"Preserved2"], @"preserved", @"Regular header is not preserved when following redirects"); + }]; + + [session finishTasksAndInvalidate]; + } + } + } + else + { + NSLog(@"/!\\ Test skipped because the NSURLSession class is not available on this OS version. Run the tests a target with a more recent OS.\n"); + } +} #endif - (void)test_NSURLSessionEphemeralConfig @@ -371,8 +422,8 @@ - (void)test_NSURLSessionEphemeralConfig XCTAssertEqualObjects(jsonResponse, json, @"Unexpected data received"); }]; - [self _test_redirect_NSURLSession:session httpMethod:@"GET" jsonBody:json delays:0.1 redirectStatusCode:301 - completion:^(NSString *redirectedRequestMethod, id redirectedRequestJSONBody, NSHTTPURLResponse *redirectHTTPResponse, id finalJSONResponse, NSError *errorResponse) + [self _test_redirect_NSURLSession:session httpMethod:@"GET" headers:nil jsonBody:json delays:0.1 redirectStatusCode:301 + completion:^(NSString *redirectedRequestMethod, NSDictionary *redirectedRequestHeaders, id redirectedRequestJSONBody, NSHTTPURLResponse *redirectHTTPResponse, id finalJSONResponse, NSError *errorResponse) { XCTAssertEqualObjects(redirectedRequestMethod, @"GET", @"Expected the HTTP method of redirected request to be GET"); XCTAssertEqualObjects(redirectedRequestJSONBody, json, @"Expected redirected request to have the same body as the original request"); @@ -404,8 +455,8 @@ - (void)test_NSURLSessionDefaultConfig_Disabled XCTAssertNil(jsonResponse, @"Data should not have been received as stubs should be disabled"); }]; - [self _test_redirect_NSURLSession:session httpMethod:@"GET" jsonBody:json delays:0.1 redirectStatusCode:301 - completion:^(NSString *redirectedRequestMethod, id redirectedRequestJSONBody, NSHTTPURLResponse *finalHTTPResponse, id finalJSONResponse, NSError *errorResponse) + [self _test_redirect_NSURLSession:session httpMethod:@"GET" headers:nil jsonBody:json delays:0.1 redirectStatusCode:301 + completion:^(NSString *redirectedRequestMethod, NSDictionary *redirectedRequestHeaders, id redirectedRequestJSONBody, NSHTTPURLResponse *finalHTTPResponse, id finalJSONResponse, NSError *errorResponse) { // Stubs were disabled for this session, so we should get an error instead of the stubs data XCTAssertNotNil(errorResponse, @"Expected error but none found");