Skip to content

Commit

Permalink
fix: Resolve Keep-Alive Redirect Handling and Add Default Path for HT…
Browse files Browse the repository at this point in the history
…TP/1.1 Requests (#23)

fix: resolve redirect issue caused by keep-alive connection by proactively closing the connection when a redirection is detected in the buffer.
fix: Add default path for HTTP 1.1 requests when no path is explicitly defined.
  • Loading branch information
AlejandroOrozco authored Dec 20, 2024
1 parent 1d36008 commit 1bdccee
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 31 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/objective-c-xcode.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
jobs:
build:
name: Build and analyse default scheme using xcodebuild command
runs-on: macos-12
runs-on: macos-14

steps:
- name: Checkout
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
jobs:
build:

runs-on: macos-12
runs-on: macos-14

steps:
- uses: actions/checkout@v3
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 0.0.9 (18–12-2024)

### Improvements

- Fixed Redirect Handling with Keep-Alive Connections: Resolved an issue where redirects could fail due to persistent keep-alive connections. The fix ensures the connection is proactively closed upon detecting a redirection in the buffer, preventing inconsistencies and ensuring smooth redirection handling.
- Added Default Path for HTTP/1.1 Requests: Introduced a default path ("/") for HTTP/1.1 requests to ensure compatibility with servers when no explicit path is specified in the request URL.

# 0.0.8 (12–06-2024)

### Improvements
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ During the current phase of this project, we only support SPM. We have plans to
dependencies: [
.package(
url: "https://github.com/twilio/twilio-verify-sna-ios.git",
.upToNextMajor(from: "0.0.8")
.upToNextMajor(from: "0.0.9")
)
]
```
Expand Down
91 changes: 67 additions & 24 deletions SNASources/CellularSession.m
Original file line number Diff line number Diff line change
Expand Up @@ -230,15 +230,18 @@ - (CellularSessionResult * _Nonnull)performRequest:(NSURL * _Nonnull)url {
}

// Create the HTTP request string
NSString *requestString = [NSString
stringWithFormat:@"GET %@%@ HTTP/1.1\r\nHost: %@%@\r\nAccept: */*\r\n",
[url path],

NSString *requestString = [NSString stringWithFormat:
@"GET %@%@ HTTP/1.1\r\n"
"Host: %@%@\r\n"
"Accept: */*\r\n"
"Connection: close\r\n"
"\r\n",
[url path].length > 0 ? [url path] : @"/",
[url query] ? [@"?" stringByAppendingString:[url query]] : @"",
[url host],
[url port] ? [@":" stringByAppendingFormat:@"%@", [url port]] : @""];

requestString = [requestString stringByAppendingString:@"Connection: close\r\n\r\n"];


const char* request = [requestString UTF8String];

char buffer[4096];
Expand Down Expand Up @@ -339,15 +342,33 @@ - (CellularSessionResult * _Nonnull)performRequest:(NSURL * _Nonnull)url {
if (status == noErr && processed > 0) {
// Append the received data to responseData
[responseData appendBytes:buffer length:processed];

// Read processed content
NSString *processedContent = [NSString stringWithUTF8String:buffer];

// Close the connection if a redirect link is already present in the buffer.
NSString *redirectLink = extractRedirectLink(processedContent);
if (redirectLink) {
close(sock);
break;
}
} else if (status == errSSLWouldBlock) {
// No more data available
SSLClose(context);
CFRelease(context);
status = noErr;
break;
} else {
// No data received
break;
if (responseData.length > 0) {
// Data was received unexpectedly after the socket was closed.
SSLClose(context);
CFRelease(context);
status = noErr;
break;
} else {
// No data was received after the socket was closed.
break;
}
}
} while (status == noErr);

Expand Down Expand Up @@ -377,24 +398,13 @@ - (CellularSessionResult * _Nonnull)performRequest:(NSURL * _Nonnull)url {

NSRange toReturnRange = NSMakeRange(prefixLocation, 1);

NSString* urlResponseCode = [response substringWithRange:toReturnRange];

// If the HTTP response contains a HTTP redirect code, obtain the redirect URL (which is found from the Location header), and return a string containing the redirect URL.
// For example, "REDIRECT:https://redirect_url.com"
if ([urlResponseCode isEqualToString:@"3"]) {
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"Location: (.*)\r\n" options:NSRegularExpressionCaseInsensitive error:NULL];

NSArray *myArray = [regex matchesInString:response options:0 range:NSMakeRange(0, [response length])] ;

NSString* redirectLink = @"";

for (NSTextCheckingResult *match in myArray) {
NSRange matchRange = [match rangeAtIndex:1];
redirectLink = [response substringWithRange:matchRange];
}

response = @"REDIRECT:";
response = [response stringByAppendingString:redirectLink];

NSString *redirectLink = extractRedirectLink(response);

if (redirectLink) {
response = redirectLink;
}

sessionResult.status = CellularSessionSuccess;
Expand All @@ -403,4 +413,37 @@ - (CellularSessionResult * _Nonnull)performRequest:(NSURL * _Nonnull)url {
return sessionResult;
}

// Function to obtain the redirection link
NSString *extractRedirectLink(NSString *response) {
// Find the HTTP status code
NSUInteger prefixLocation = [response rangeOfString:@"HTTP/"].location + 9;
if (prefixLocation >= response.length) {
return nil; // Invalid or malformed response
}

NSRange toReturnRange = NSMakeRange(prefixLocation, 1);
NSString *urlResponseCode = [response substringWithRange:toReturnRange];

// Check if the status code starts with "3" (redirect codes)
if ([urlResponseCode isEqualToString:@"3"]) {
// Use a regular expression to find the Location header
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(?i)Location: (.*)\r\n"
options:0
error:nil];
NSArray<NSTextCheckingResult *> *matches = [regex matchesInString:response
options:0
range:NSMakeRange(0, response.length)];
if (matches.count > 0) {
// Extract the first match for the Location header
NSTextCheckingResult *match = matches.firstObject;
NSRange locationRange = [match rangeAtIndex:1];
NSString *redirectLink = [response substringWithRange:locationRange];
return [NSString stringWithFormat:@"REDIRECT:%@", redirectLink];
}
}

// No redirection or Location header found
return nil;
}

@end
1 change: 1 addition & 0 deletions SNASources/include/SocketAddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#define SocketAddress_h

#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>

typedef struct sockaddr *sockaddr_t;

Expand Down
2 changes: 1 addition & 1 deletion Sources/TwilioVerifySNAConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@
import Foundation

public struct TwilioVerifySNAConfig {
public static let version = "0.0.8"
public static let version = "0.0.9"
}
Original file line number Diff line number Diff line change
Expand Up @@ -398,4 +398,4 @@ extension PhoneNumberViewController {
}

/// Not required for the SDK implementation.
private let sampleAppVersion = "0.0.8"
private let sampleAppVersion = "0.0.9"
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@
"@executable_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 0.0.8;
MARKETING_VERSION = 0.0.9;
PRODUCT_BUNDLE_IDENTIFIER = com.twilio.TwilioVerifySNADemo;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -448,7 +448,7 @@
"@executable_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 0.0.8;
MARKETING_VERSION = 0.0.9;
PRODUCT_BUNDLE_IDENTIFIER = com.twilio.TwilioVerifySNADemo;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down

0 comments on commit 1bdccee

Please sign in to comment.