diff --git a/Limelight/Network/DiscoveryManager.m b/Limelight/Network/DiscoveryManager.m index 05f5b1d..de17574 100644 --- a/Limelight/Network/DiscoveryManager.m +++ b/Limelight/Network/DiscoveryManager.m @@ -41,7 +41,7 @@ - (void) discoverHost:(NSString *)hostAddress withCallback:(void (^)(Host *, NSString*))callback { HttpManager* hMan = [[HttpManager alloc] initWithHost:hostAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert]; ServerInfoResponse* serverInfoResponse = [[ServerInfoResponse alloc] init]; - [hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResponse withUrlRequest:[hMan newServerInfoRequest]]]; + [hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResponse withUrlRequest:[hMan newServerInfoRequest] fallbackError:401 fallbackRequest:[hMan newHttpServerInfoRequest]]]; Host* host = nil; if ([serverInfoResponse isStatusOk]) { diff --git a/Limelight/Network/DiscoveryWorker.m b/Limelight/Network/DiscoveryWorker.m index e871aca..9d9314e 100644 --- a/Limelight/Network/DiscoveryWorker.m +++ b/Limelight/Network/DiscoveryWorker.m @@ -68,7 +68,8 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds cert:_cert]; ServerInfoResponse* response = [[ServerInfoResponse alloc] init]; [hMan executeRequestSynchronously:[HttpRequest requestForResponse:response - withUrlRequest:[hMan newServerInfoRequest]]]; + withUrlRequest:[hMan newServerInfoRequest] + fallbackError:401 fallbackRequest:[hMan newHttpServerInfoRequest]]]; return response; } diff --git a/Limelight/Network/HttpManager.h b/Limelight/Network/HttpManager.h index 29f15aa..d72a618 100644 --- a/Limelight/Network/HttpManager.h +++ b/Limelight/Network/HttpManager.h @@ -11,7 +11,7 @@ #import "HttpResponse.h" #import "HttpRequest.h" -@interface HttpManager : NSObject +@interface HttpManager : NSObject - (id) initWithHost:(NSString*) host uniqueId:(NSString*) uniqueId deviceName:(NSString*) deviceName cert:(NSData*) cert; - (NSURLRequest*) newPairRequest:(NSData*)salt; @@ -22,12 +22,12 @@ - (NSURLRequest*) newPairChallenge; - (NSURLRequest*) newAppListRequest; - (NSURLRequest*) newServerInfoRequest; +- (NSURLRequest*) newHttpServerInfoRequest; - (NSURLRequest*) newLaunchRequest:(NSString*)appId width:(int)width height:(int)height refreshRate:(int)refreshRate rikey:(NSString*)rikey rikeyid:(int)rikeyid; - (NSURLRequest*) newResumeRequestWithRiKey:(NSString*)riKey riKeyId:(int)riKeyId; - (NSURLRequest*) newQuitAppRequest; - (NSURLRequest*) newAppAssetRequestWithAppId:(NSString*)appId; - (void) executeRequestSynchronously:(HttpRequest*)request; -- (void) executeRequest:(HttpRequest*)request; @end diff --git a/Limelight/Network/HttpManager.m b/Limelight/Network/HttpManager.m index 9e580d7..da6f20a 100644 --- a/Limelight/Network/HttpManager.m +++ b/Limelight/Network/HttpManager.m @@ -15,7 +15,9 @@ #include @implementation HttpManager { - NSString* _baseURL; + NSURLSession* _urlSession; + NSString* _baseHTTPURL; + NSString* _baseHTTPSURL; NSString* _host; NSString* _uniqueId; NSString* _deviceName; @@ -27,7 +29,8 @@ BOOL _errorOccurred; } -static const NSString* PORT = @"47984"; +static const NSString* HTTP_PORT = @"47989"; +static const NSString* HTTPS_PORT = @"47984"; + (NSData*) fixXmlVersion:(NSData*) xmlData { NSString* dataString = [[NSString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding]; @@ -42,30 +45,57 @@ static const NSString* PORT = @"47984"; _uniqueId = uniqueId; _deviceName = deviceName; _cert = cert; - _baseURL = [NSString stringWithFormat:@"https://%@:%@", host, PORT]; + _baseHTTPURL = [NSString stringWithFormat:@"http://%@:%@", host, HTTP_PORT]; + _baseHTTPSURL = [NSString stringWithFormat:@"https://%@:%@", host, HTTPS_PORT]; _requestLock = dispatch_semaphore_create(0); _respData = [[NSMutableData alloc] init]; + NSURLSessionConfiguration* config = [NSURLSessionConfiguration ephemeralSessionConfiguration]; + _urlSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil]; return self; } - (void) executeRequestSynchronously:(HttpRequest*)request { Log(LOG_D, @"Making Request: %@", request); [_respData setLength:0]; - dispatch_sync(dispatch_get_main_queue(), ^{ - [NSURLConnection connectionWithRequest:request.request delegate:self]; - }); + [[_urlSession dataTaskWithRequest:request.request completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) { + + if (error != NULL) { + Log(LOG_D, @"Connection error: %@", error); + _errorOccurred = true; + } + else { + Log(LOG_D, @"Received response: %@", response); + + if (data != NULL) { + Log(LOG_D, @"\n\nReceived data: %@\n\n", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); + [_respData appendData:data]; + if ([[NSString alloc] initWithData:_respData encoding:NSUTF8StringEncoding] != nil) { + _requestResp = [HttpManager fixXmlVersion:_respData]; + } else { + _requestResp = _respData; + } + } + } + + dispatch_semaphore_signal(_requestLock); + }] resume]; dispatch_semaphore_wait(_requestLock, DISPATCH_TIME_FOREVER); if (!_errorOccurred && request.response) { [request.response populateWithData:_requestResp]; + + // If the fallback error code was detected, issue the fallback request + if (request.response.statusCode == request.fallbackError && request.fallbackRequest != NULL) { + Log(LOG_D, @"Request failed with fallback error code: %d", request.fallbackError); + request.request = request.fallbackRequest; + request.fallbackError = 0; + request.fallbackRequest = NULL; + [self executeRequestSynchronously:request]; + } } _errorOccurred = false; } -- (void) executeRequest:(HttpRequest*)request { - [NSURLConnection connectionWithRequest:request.request delegate:self]; -} - - (NSURLRequest*) createRequestFromString:(NSString*) urlString enableTimeout:(BOOL)normalTimeout { NSURL* url = [[NSURL alloc] initWithString:urlString]; NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url]; @@ -82,67 +112,72 @@ static const NSString* PORT = @"47984"; - (NSURLRequest*) newPairRequest:(NSData*)salt { NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&phrase=getservercert&salt=%@&clientcert=%@", - _baseURL, _uniqueId, _deviceName, [self bytesToHex:salt], [self bytesToHex:_cert]]; + _baseHTTPSURL, _uniqueId, _deviceName, [self bytesToHex:salt], [self bytesToHex:_cert]]; // This call blocks while waiting for the user to input the PIN on the PC return [self createRequestFromString:urlString enableTimeout:FALSE]; } - (NSURLRequest*) newUnpairRequest { - NSString* urlString = [NSString stringWithFormat:@"%@/unpair?uniqueid=%@", _baseURL, _uniqueId]; + NSString* urlString = [NSString stringWithFormat:@"%@/unpair?uniqueid=%@", _baseHTTPSURL, _uniqueId]; return [self createRequestFromString:urlString enableTimeout:TRUE]; } - (NSURLRequest*) newChallengeRequest:(NSData*)challenge { NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&clientchallenge=%@", - _baseURL, _uniqueId, _deviceName, [self bytesToHex:challenge]]; + _baseHTTPSURL, _uniqueId, _deviceName, [self bytesToHex:challenge]]; return [self createRequestFromString:urlString enableTimeout:TRUE]; } - (NSURLRequest*) newChallengeRespRequest:(NSData*)challengeResp { NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&serverchallengeresp=%@", - _baseURL, _uniqueId, _deviceName, [self bytesToHex:challengeResp]]; + _baseHTTPSURL, _uniqueId, _deviceName, [self bytesToHex:challengeResp]]; return [self createRequestFromString:urlString enableTimeout:TRUE]; } - (NSURLRequest*) newClientSecretRespRequest:(NSString*)clientPairSecret { - NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&clientpairingsecret=%@", _baseURL, _uniqueId, _deviceName, clientPairSecret]; + NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&clientpairingsecret=%@", _baseHTTPSURL, _uniqueId, _deviceName, clientPairSecret]; return [self createRequestFromString:urlString enableTimeout:TRUE]; } - (NSURLRequest*) newPairChallenge { - NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&phrase=pairchallenge", _baseURL, _uniqueId, _deviceName]; + NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&phrase=pairchallenge", _baseHTTPSURL, _uniqueId, _deviceName]; return [self createRequestFromString:urlString enableTimeout:TRUE]; } - (NSURLRequest *)newAppListRequest { - NSString* urlString = [NSString stringWithFormat:@"%@/applist?uniqueid=%@", _baseURL, _uniqueId]; + NSString* urlString = [NSString stringWithFormat:@"%@/applist?uniqueid=%@", _baseHTTPSURL, _uniqueId]; return [self createRequestFromString:urlString enableTimeout:TRUE]; } - (NSURLRequest *)newServerInfoRequest { - NSString* urlString = [NSString stringWithFormat:@"%@/serverinfo?uniqueid=%@", _baseURL, _uniqueId]; + NSString* urlString = [NSString stringWithFormat:@"%@/serverinfo?uniqueid=%@", _baseHTTPSURL, _uniqueId]; + return [self createRequestFromString:urlString enableTimeout:TRUE]; +} + +- (NSURLRequest *)newHttpServerInfoRequest { + NSString* urlString = [NSString stringWithFormat:@"%@/serverinfo", _baseHTTPURL]; return [self createRequestFromString:urlString enableTimeout:TRUE]; } - (NSURLRequest*) newLaunchRequest:(NSString*)appId width:(int)width height:(int)height refreshRate:(int)refreshRate rikey:(NSString*)rikey rikeyid:(int)rikeyid { - NSString* urlString = [NSString stringWithFormat:@"%@/launch?uniqueid=%@&appid=%@&mode=%dx%dx%d&additionalStates=1&sops=1&rikey=%@&rikeyid=%d", _baseURL, _uniqueId, appId, width, height, refreshRate, rikey, rikeyid]; + NSString* urlString = [NSString stringWithFormat:@"%@/launch?uniqueid=%@&appid=%@&mode=%dx%dx%d&additionalStates=1&sops=1&rikey=%@&rikeyid=%d", _baseHTTPSURL, _uniqueId, appId, width, height, refreshRate, rikey, rikeyid]; // This blocks while the app is launching return [self createRequestFromString:urlString enableTimeout:FALSE]; } - (NSURLRequest*) newResumeRequestWithRiKey:(NSString*)riKey riKeyId:(int)riKeyId { - NSString* urlString = [NSString stringWithFormat:@"%@/resume?uniqueid=%@&rikey=%@&rikeyid=%d", _baseURL, _uniqueId, riKey, riKeyId]; + NSString* urlString = [NSString stringWithFormat:@"%@/resume?uniqueid=%@&rikey=%@&rikeyid=%d", _baseHTTPSURL, _uniqueId, riKey, riKeyId]; // This blocks while the app is resuming return [self createRequestFromString:urlString enableTimeout:FALSE]; } - (NSURLRequest*) newQuitAppRequest { - NSString* urlString = [NSString stringWithFormat:@"%@/cancel?uniqueid=%@", _baseURL, _uniqueId]; + NSString* urlString = [NSString stringWithFormat:@"%@/cancel?uniqueid=%@", _baseHTTPSURL, _uniqueId]; return [self createRequestFromString:urlString enableTimeout:FALSE]; } - (NSURLRequest*) newAppAssetRequestWithAppId:(NSString *)appId { - NSString* urlString = [NSString stringWithFormat:@"%@/appasset?uniqueid=%@&appid=%@&AssetType=2&AssetIdx=0", _baseURL, _uniqueId, appId]; + NSString* urlString = [NSString stringWithFormat:@"%@/appasset?uniqueid=%@&appid=%@&AssetType=2&AssetIdx=0", _baseHTTPSURL, _uniqueId, appId]; return [self createRequestFromString:urlString enableTimeout:FALSE]; } @@ -155,33 +190,6 @@ static const NSString* PORT = @"47984"; return hex; } -- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { - Log(LOG_D, @"Received response: %@", response); -} - -- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { - Log(LOG_D, @"\n\nReceived data: %@\n\n", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); - [_respData appendData:data]; -} - -- (void)connectionDidFinishLoading:(NSURLConnection *)connection { - if ([[NSString alloc] initWithData:_respData encoding:NSUTF8StringEncoding] != nil) { - _requestResp = [HttpManager fixXmlVersion:_respData]; - } else { - _requestResp = _respData; - } - dispatch_semaphore_signal(_requestLock); -} - -- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { - SecIdentityRef identity = [self getClientCertificate]; - NSArray *certArray = [self getCertificate:identity]; - - NSURLCredential *newCredential = [NSURLCredential credentialWithIdentity:identity certificates:certArray persistence:NSURLCredentialPersistencePermanent]; - - [challenge.sender useCredential:newCredential forAuthenticationChallenge:challenge]; -} - // Returns an array containing the certificate - (NSArray*)getCertificate:(SecIdentityRef) identity { SecCertificateRef certificate = nil; @@ -217,10 +225,25 @@ static const NSString* PORT = @"47984"; return identityApp; } -- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { - Log(LOG_D, @"connection error: %@", error); - _errorOccurred = true; - dispatch_semaphore_signal(_requestLock); +- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(nonnull void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * __nullable))completionHandler { + // Allow untrusted server certificates + if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) + { + completionHandler(NSURLSessionAuthChallengeUseCredential, + [NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]); + } + // Respond to client certificate challenge with our certificate + else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) + { + SecIdentityRef identity = [self getClientCertificate]; + NSArray* certArray = [self getCertificate:identity]; + NSURLCredential* newCredential = [NSURLCredential credentialWithIdentity:identity certificates:certArray persistence:NSURLCredentialPersistencePermanent]; + completionHandler(NSURLSessionAuthChallengeUseCredential, newCredential); + } + else + { + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, NULL); + } } @end diff --git a/Limelight/Network/HttpRequest.h b/Limelight/Network/HttpRequest.h index bead208..743b58b 100644 --- a/Limelight/Network/HttpRequest.h +++ b/Limelight/Network/HttpRequest.h @@ -13,7 +13,10 @@ @property (nonatomic) id response; @property (nonatomic) NSURLRequest* request; +@property (nonatomic) int fallbackError; +@property (nonatomic) NSURLRequest* fallbackRequest; ++ (instancetype) requestForResponse:(id)response withUrlRequest:(NSURLRequest*)req fallbackError:(int)error fallbackRequest:(NSURLRequest*) fallbackReq; + (instancetype) requestForResponse:(id)response withUrlRequest:(NSURLRequest*)req; + (instancetype) requestWithUrlRequest:(NSURLRequest*)req; diff --git a/Limelight/Network/HttpRequest.m b/Limelight/Network/HttpRequest.m index 69298ed..afb1c89 100644 --- a/Limelight/Network/HttpRequest.m +++ b/Limelight/Network/HttpRequest.m @@ -25,4 +25,13 @@ return request; } ++ (HttpRequest*) requestForResponse:(id)response withUrlRequest:(NSURLRequest*)req fallbackError:(int)error fallbackRequest:(NSURLRequest*) fallbackReq { + HttpRequest* request = [[HttpRequest alloc] init]; + request.request = req; + request.response = response; + request.fallbackError = error; + request.fallbackRequest = fallbackReq; + return request; +} + @end diff --git a/Limelight/Network/PairManager.m b/Limelight/Network/PairManager.m index 93e6995..82ff2b2 100644 --- a/Limelight/Network/PairManager.m +++ b/Limelight/Network/PairManager.m @@ -31,7 +31,8 @@ - (void) main { ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init]; - [_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[_httpManager newServerInfoRequest]]]; + [_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[_httpManager newServerInfoRequest] + fallbackError:401 fallbackRequest:[_httpManager newHttpServerInfoRequest]]]; if (serverInfoResp == nil) { [_callback pairFailed:@"Unable to connect to PC"]; return; @@ -60,7 +61,7 @@ } NSInteger pairedStatus; if (![pairResp getIntTag:@"paired" value:&pairedStatus] || !pairedStatus) { - [_httpManager executeRequest:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; + [_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; [_callback pairFailed:@"Pairing was declined by the target."]; return; } @@ -79,7 +80,7 @@ return; } if (![challengeResp getIntTag:@"paired" value:&pairedStatus] || !pairedStatus) { - [_httpManager executeRequest:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; + [_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; [_callback pairFailed:@"Pairing stage #2 failed"]; return; } @@ -100,7 +101,7 @@ return; } if (![secretResp getIntTag:@"paired" value:&pairedStatus] || !pairedStatus) { - [_httpManager executeRequest:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; + [_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; [_callback pairFailed:@"Pairing stage #3 failed"]; return; } @@ -110,14 +111,14 @@ NSData* serverSignature = [serverSecretResp subdataWithRange:NSMakeRange(16, 256)]; if (![cryptoMan verifySignature:serverSecret withSignature:serverSignature andCert:[Utils hexToBytes:plainCert]]) { - [_httpManager executeRequest:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; + [_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; [_callback pairFailed:@"Server certificate invalid"]; return; } NSData* serverChallengeRespHash = [cryptoMan SHA1HashData:[self concatData:[self concatData:randomChallenge with:[CryptoManager getSignatureFromCert:[Utils hexToBytes:plainCert]]] with:serverSecret]]; if (![serverChallengeRespHash isEqual:serverResponse]) { - [_httpManager executeRequest:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; + [_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; [_callback pairFailed:@"Incorrect PIN"]; return; } @@ -129,7 +130,7 @@ return; } if (![clientSecretResp getIntTag:@"paired" value:&pairedStatus] || !pairedStatus) { - [_httpManager executeRequest:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; + [_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; [_callback pairFailed:@"Pairing stage #4 failed"]; return; } @@ -140,7 +141,7 @@ return; } if (![clientPairChallengeResp getIntTag:@"paired" value:&pairedStatus] || !pairedStatus) { - [_httpManager executeRequest:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; + [_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; [_callback pairFailed:@"Pairing stage #5 failed"]; return; } @@ -149,11 +150,11 @@ - (BOOL) verifyResponseStatus:(HttpResponse*)resp { if (resp == nil) { - [_httpManager executeRequest:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; + [_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; [_callback pairFailed:@"Network error occured."]; return false; } else if (![resp isStatusOk]) { - [_httpManager executeRequest:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; + [_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; [_callback pairFailed:resp.statusMessage]; return false; } else { diff --git a/Limelight/Stream/StreamManager.m b/Limelight/Stream/StreamManager.m index 3308ad1..d9087c3 100644 --- a/Limelight/Stream/StreamManager.m +++ b/Limelight/Stream/StreamManager.m @@ -45,7 +45,8 @@ cert:cert]; ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init]; - [hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[hMan newServerInfoRequest]]]; + [hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[hMan newServerInfoRequest] + fallbackError:401 fallbackRequest:[hMan newHttpServerInfoRequest]]]; NSString* currentGame = [serverInfoResp getStringTag:@"currentgame"]; NSString* pairStatus = [serverInfoResp getStringTag:@"PairStatus"]; NSString* currentClient = [serverInfoResp getStringTag:@"CurrentClient"]; diff --git a/Limelight/ViewControllers/MainFrameViewController.m b/Limelight/ViewControllers/MainFrameViewController.m index 929b30b..e188607 100644 --- a/Limelight/ViewControllers/MainFrameViewController.m +++ b/Limelight/ViewControllers/MainFrameViewController.m @@ -133,7 +133,8 @@ static NSArray* appList; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ HttpManager* hMan = [[HttpManager alloc] initWithHost:host.address uniqueId:_uniqueId deviceName:deviceName cert:_cert]; ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init]; - [hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[hMan newServerInfoRequest]]]; + [hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[hMan newServerInfoRequest] + fallbackError:401 fallbackRequest:[hMan newHttpServerInfoRequest]]]; if (serverInfoResp == nil || ![serverInfoResp isStatusOk]) { Log(LOG_W, @"Failed to get server info: %@", serverInfoResp.statusMessage); [self hideLoadingFrame]; @@ -160,7 +161,7 @@ static NSArray* appList; [longClickAlert addAction:[UIAlertAction actionWithTitle:@"Unpair" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ HttpManager* hMan = [[HttpManager alloc] initWithHost:host.address uniqueId:_uniqueId deviceName:deviceName cert:_cert]; - [hMan executeRequest:[HttpRequest requestWithUrlRequest:[hMan newUnpairRequest]]]; + [hMan executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[hMan newUnpairRequest]]]; }); }]]; } else {