Replace HTTPSource with a new reader based on NSURLSession, which actually does support HTTPS. This portion of the player now requires macOS 10.9.
parent
17bb70e729
commit
443aa05f88
|
@ -12,10 +12,20 @@
|
|||
|
||||
@class HTTPConnection;
|
||||
|
||||
@interface HTTPSource : NSObject <CogSource>
|
||||
@interface HTTPSource : NSObject <CogSource, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
|
||||
{
|
||||
HTTPConnection *_connection;
|
||||
NSOperationQueue * queue;
|
||||
|
||||
NSURL * URL;
|
||||
NSURLSession * session;
|
||||
NSURLSessionDataTask * task;
|
||||
|
||||
Boolean cancelled;
|
||||
Boolean errorOccurred;
|
||||
|
||||
NSMutableArray * bufferedData;
|
||||
|
||||
long _bytesBuffered;
|
||||
long _byteCount;
|
||||
|
||||
NSString *_mimeType;
|
||||
|
|
|
@ -3,32 +3,80 @@
|
|||
// HTTPSource
|
||||
//
|
||||
// Created by Vincent Spader on 3/1/07.
|
||||
// Copyright 2007 __MyCompanyName__. All rights reserved.
|
||||
// Replaced by Christopher Snowhill on 3/7/20.
|
||||
// Copyright 2020 __LoSnoCo__. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HTTPSource.h"
|
||||
#import "HTTPConnection.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
#define BUFFER_SIZE 131072
|
||||
|
||||
@implementation HTTPSource
|
||||
|
||||
- (NSURLSession *)createSession
|
||||
{
|
||||
queue = [[NSOperationQueue alloc] init];
|
||||
[queue setMaxConcurrentOperationCount:1];
|
||||
|
||||
NSURLSession *session = nil;
|
||||
session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
|
||||
delegate:self
|
||||
delegateQueue:queue];
|
||||
return session;
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
dataTask:(NSURLSessionDataTask *)dataTask
|
||||
didReceiveData:(NSData *)data{
|
||||
long bytesBuffered = 0;
|
||||
if (cancelled) return;
|
||||
@synchronized(bufferedData) {
|
||||
[bufferedData addObject:data];
|
||||
_bytesBuffered += [data length];
|
||||
bytesBuffered = _bytesBuffered;
|
||||
}
|
||||
if (bytesBuffered > BUFFER_SIZE) {
|
||||
[task suspend];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
|
||||
willCacheResponse:(NSCachedURLResponse *)proposedResponse
|
||||
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler{
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
task:(NSURLSessionTask *)task
|
||||
didCompleteWithError:(NSError *)error{
|
||||
cancelled = YES;
|
||||
errorOccurred = YES;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSURL *)url
|
||||
{
|
||||
_connection = [[HTTPConnection alloc] initWithURL:url];
|
||||
cancelled = NO;
|
||||
errorOccurred = NO;
|
||||
|
||||
// Note: The User-Agent CANNOT contain the string "Mozilla" or Shoutcast/Icecast will serve up HTML
|
||||
NSString *userAgent = [NSString stringWithFormat:@"Cog %@", [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey]];
|
||||
[_connection setValue:userAgent forRequestHeader:@"User-Agent"];
|
||||
[_connection setValue:@"close" forRequestHeader:@"Connection"];
|
||||
[_connection setValue:@"*/*" forRequestHeader:@"Accept"];
|
||||
bufferedData = [[NSMutableArray alloc] init];
|
||||
|
||||
BOOL success = [_connection connect];
|
||||
if (NO == success) {
|
||||
return NO;
|
||||
URL = url;
|
||||
NSURLRequest * request = [NSURLRequest requestWithURL:url];
|
||||
session = [self createSession];
|
||||
task = [session dataTaskWithRequest:request];
|
||||
[task resume];
|
||||
|
||||
NSURLResponse * response = nil;
|
||||
while (!response) {
|
||||
response = [task response];
|
||||
if (response) break;
|
||||
if (errorOccurred) return NO;
|
||||
usleep(100);
|
||||
}
|
||||
if (response) {
|
||||
_mimeType = [response MIMEType];
|
||||
}
|
||||
|
||||
_mimeType = [[_connection valueForResponseHeader:@"Content-type"] copy];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
@ -57,14 +105,55 @@
|
|||
- (long)read:(void *)buffer amount:(long)amount
|
||||
{
|
||||
long totalRead = 0;
|
||||
long bytesBuffered = 0;
|
||||
|
||||
while (totalRead < amount) {
|
||||
NSInteger amountReceived = [_connection receiveData:((uint8_t *)buffer) + totalRead amount:amount - totalRead];
|
||||
NSData * dataBlock = nil;
|
||||
@synchronized(bufferedData) {
|
||||
if ([bufferedData count])
|
||||
dataBlock = [bufferedData objectAtIndex:0];
|
||||
}
|
||||
if (!dataBlock) {
|
||||
if (errorOccurred) return totalRead;
|
||||
if (cancelled) return 0;
|
||||
usleep(1000);
|
||||
continue;
|
||||
}
|
||||
NSInteger amountReceived = [dataBlock length];
|
||||
if (amountReceived <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
totalRead += amountReceived;
|
||||
NSInteger amountUsed = amountReceived;
|
||||
|
||||
if (amountUsed > (amount - totalRead) )
|
||||
amountUsed = amount - totalRead;
|
||||
|
||||
const void * dataBytes = [dataBlock bytes];
|
||||
memcpy(((uint8_t *)buffer) + totalRead, dataBytes, amountUsed);
|
||||
|
||||
if (amountUsed < amountReceived) {
|
||||
NSData * dataOut = [NSData dataWithBytes:(((uint8_t *)dataBytes) + amountUsed) length:(amountReceived - amountUsed)];
|
||||
@synchronized(bufferedData) {
|
||||
[bufferedData removeObjectAtIndex:0];
|
||||
[bufferedData insertObject:dataOut atIndex:0];
|
||||
_bytesBuffered -= amountUsed;
|
||||
bytesBuffered = _bytesBuffered;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@synchronized(bufferedData) {
|
||||
[bufferedData removeObjectAtIndex:0];
|
||||
_bytesBuffered -= amountUsed;
|
||||
bytesBuffered = _bytesBuffered;
|
||||
}
|
||||
}
|
||||
|
||||
if (bytesBuffered <= (BUFFER_SIZE * 3 / 4)) {
|
||||
[task resume];
|
||||
}
|
||||
|
||||
totalRead += amountUsed;
|
||||
}
|
||||
|
||||
_byteCount += totalRead;
|
||||
|
@ -74,8 +163,10 @@
|
|||
|
||||
- (void)close
|
||||
{
|
||||
[_connection close];
|
||||
_connection = nil;
|
||||
cancelled = YES;
|
||||
|
||||
[task cancel];
|
||||
task = nil;
|
||||
|
||||
_mimeType = nil;
|
||||
}
|
||||
|
@ -88,7 +179,7 @@
|
|||
|
||||
- (NSURL *)url
|
||||
{
|
||||
return [_connection URL];
|
||||
return URL;
|
||||
}
|
||||
|
||||
+ (NSArray *)schemes
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1716067D0F624E09008FA424 /* Socket.m in Sources */ = {isa = PBXBuildFile; fileRef = 1716067C0F624E09008FA424 /* Socket.m */; };
|
||||
1716068A0F624E27008FA424 /* HTTPConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 171606870F624E27008FA424 /* HTTPConnection.m */; };
|
||||
1716093A0F627F02008FA424 /* HTTPSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 171609390F627F02008FA424 /* HTTPSource.m */; };
|
||||
8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
@ -29,10 +27,6 @@
|
|||
089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
|
||||
089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
|
||||
1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
|
||||
1716067B0F624E09008FA424 /* Socket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Socket.h; sourceTree = "<group>"; };
|
||||
1716067C0F624E09008FA424 /* Socket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Socket.m; sourceTree = "<group>"; };
|
||||
171606860F624E27008FA424 /* HTTPConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPConnection.h; sourceTree = "<group>"; };
|
||||
171606870F624E27008FA424 /* HTTPConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTTPConnection.m; sourceTree = "<group>"; };
|
||||
171609390F627F02008FA424 /* HTTPSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTTPSource.m; sourceTree = "<group>"; };
|
||||
17ADB60C0B97A74800257CA2 /* HTTPSource.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = HTTPSource.h; sourceTree = "<group>"; };
|
||||
17ADB6340B97A8B400257CA2 /* Plugin.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Plugin.h; path = ../../Audio/Plugin.h; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -59,7 +53,6 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
08FB77AFFE84173DC02AAC07 /* Classes */,
|
||||
1716067A0F624E09008FA424 /* Utils */,
|
||||
32C88E010371C26100C91783 /* Other Sources */,
|
||||
089C167CFE841241C02AAC07 /* Resources */,
|
||||
089C1671FE841209C02AAC07 /* Frameworks and Libraries */,
|
||||
|
@ -114,17 +107,6 @@
|
|||
name = "Other Frameworks";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1716067A0F624E09008FA424 /* Utils */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
171606860F624E27008FA424 /* HTTPConnection.h */,
|
||||
171606870F624E27008FA424 /* HTTPConnection.m */,
|
||||
1716067B0F624E09008FA424 /* Socket.h */,
|
||||
1716067C0F624E09008FA424 /* Socket.m */,
|
||||
);
|
||||
path = Utils;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
19C28FB8FE9D52D311CA2CBB /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -209,8 +191,6 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1716067D0F624E09008FA424 /* Socket.m in Sources */,
|
||||
1716068A0F624E27008FA424 /* HTTPConnection.m in Sources */,
|
||||
1716093A0F627F02008FA424 /* HTTPSource.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -232,6 +212,7 @@
|
|||
GCC_PREFIX_HEADER = HTTPSource_Prefix.pch;
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Library/Bundles";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cogx.httpsource;
|
||||
PRODUCT_NAME = HTTPSource;
|
||||
SDKROOT = macosx;
|
||||
|
@ -252,6 +233,7 @@
|
|||
GCC_PREFIX_HEADER = HTTPSource_Prefix.pch;
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Library/Bundles";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cogx.httpsource;
|
||||
PRODUCT_NAME = HTTPSource;
|
||||
SDKROOT = macosx;
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
//
|
||||
// HTTPConnection.h
|
||||
// HTTPSource
|
||||
//
|
||||
// Created by Vincent Spader on 3/6/09.
|
||||
// Copyright 2009 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "Socket.h"
|
||||
|
||||
@interface HTTPConnection : NSObject {
|
||||
Socket *_socket;
|
||||
NSURL *_URL;
|
||||
|
||||
NSMutableDictionary *_requestHeaders;
|
||||
NSMutableDictionary *_responseHeaders;
|
||||
|
||||
uint8_t *_buffer;
|
||||
NSInteger _bufferSize;
|
||||
}
|
||||
|
||||
- (id)initWithURL:(NSURL *)url;
|
||||
|
||||
- (BOOL)connect;
|
||||
- (void)close;
|
||||
|
||||
- (NSInteger)receiveData:(void *)bytes amount:(NSInteger)amount;
|
||||
|
||||
- (void)setValue:(NSString *)value forRequestHeader:(NSString *)header;
|
||||
- (NSString *)valueForResponseHeader:(NSString *)header;
|
||||
|
||||
@property(copy) NSURL *URL;
|
||||
|
||||
@end
|
|
@ -1,303 +0,0 @@
|
|||
//
|
||||
// HTTPConnection.m
|
||||
// HTTPSource
|
||||
//
|
||||
// Created by Vincent Spader on 3/6/09.
|
||||
// Copyright 2009 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HTTPConnection.h"
|
||||
|
||||
#include <netdb.h>
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
@implementation HTTPConnection
|
||||
|
||||
@synthesize URL = _URL;
|
||||
|
||||
- (id)initWithURL:(NSURL *)url
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
[self setURL:url];
|
||||
|
||||
_requestHeaders = nil;
|
||||
_responseHeaders = nil;
|
||||
_buffer = NULL;
|
||||
_bufferSize = 0;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self setURL:nil];
|
||||
}
|
||||
|
||||
- (void)setValue:(NSString *)value forRequestHeader:(NSString *)header
|
||||
{
|
||||
if (nil == _requestHeaders) {
|
||||
_requestHeaders = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
|
||||
[_requestHeaders setObject:value forKey:header];
|
||||
}
|
||||
|
||||
// Only filled in after a successful -connect
|
||||
- (NSString *)valueForResponseHeader:(NSString *)header
|
||||
{
|
||||
return [_responseHeaders objectForKey:[header lowercaseString]];
|
||||
}
|
||||
|
||||
- (NSString *)_receiveLine
|
||||
{
|
||||
const NSInteger requestAmount = 1024;
|
||||
|
||||
void *newLine = strnstr((const char *)_buffer, "\r\n", _bufferSize);
|
||||
while (NULL == newLine) {
|
||||
NSInteger bufferAmount = _bufferSize;
|
||||
|
||||
// Note that _bufferSize may be 0 and _buffer may be NULL
|
||||
_bufferSize += requestAmount;
|
||||
_buffer = realloc(_buffer, _bufferSize);
|
||||
if (NULL == _buffer) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSInteger amountReceived = [_socket receive:((uint8_t *)_buffer)+bufferAmount amount:requestAmount];
|
||||
if (amountReceived <= 0) {
|
||||
// -close will free _buffer
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (amountReceived < requestAmount) {
|
||||
_bufferSize = bufferAmount + amountReceived;
|
||||
}
|
||||
|
||||
newLine = strnstr((const char *)_buffer, "\r\n", _bufferSize);
|
||||
}
|
||||
|
||||
NSInteger lineLength = ((uint8_t *)newLine - (uint8_t *)_buffer);
|
||||
|
||||
// We are using ASCII encoding here because some Icecast servers will insert a random 0xaa or two into the headers
|
||||
// Or I'm an idiot who doesn't know how to count (fixed now), but I don't remember what site I was seeing this on, so I can't really check.
|
||||
NSString *line = [[NSString alloc] initWithBytes:_buffer length:lineLength encoding:NSASCIIStringEncoding];
|
||||
DLog(@"Received line: \"%@\"", line);
|
||||
|
||||
memmove(_buffer, _buffer + lineLength + 2, _bufferSize - lineLength); // + 2 to skip the newline!
|
||||
|
||||
_bufferSize -= (lineLength + 2); // +2 since we also skipped the newline
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
- (BOOL)_readResponse
|
||||
{
|
||||
// Clear out any old response headers
|
||||
_responseHeaders = nil;
|
||||
|
||||
// Fetch the first line so we can parse the status code
|
||||
NSString *firstLine = [self _receiveLine];
|
||||
if (nil == firstLine) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Get the status code!
|
||||
NSInteger statusCode = 0;
|
||||
NSScanner *scanner = [[NSScanner alloc] initWithString:firstLine];
|
||||
|
||||
// Scan up to the space and the number afterwards
|
||||
BOOL success = ([scanner scanUpToString:@" " intoString:nil] && [scanner scanInteger:&statusCode]);
|
||||
if (NO == success) {
|
||||
// Failed to retrieve status code.
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Prepare for response!
|
||||
_responseHeaders = [[NSMutableDictionary alloc] init];
|
||||
|
||||
// Go through the response headers
|
||||
BOOL foundEndOfHeaders = NO;
|
||||
while (NO == foundEndOfHeaders)
|
||||
{
|
||||
NSString *line = [self _receiveLine];
|
||||
if (nil == line) {
|
||||
// Error receiving data. Let's get out of here!
|
||||
DLog(@"Headers ended prematurely");
|
||||
break;
|
||||
}
|
||||
|
||||
if ([@"" isEqualToString:line]) {
|
||||
// We have \n\n, end of headers!
|
||||
foundEndOfHeaders = YES;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add the header to the dict
|
||||
NSString *key = nil;
|
||||
NSString *value = nil;
|
||||
|
||||
NSScanner *scanner = [[NSScanner alloc] initWithString:line];
|
||||
BOOL success = ([scanner scanUpToString:@":" intoString:&key] && [scanner scanString:@":" intoString:nil] && [scanner scanUpToString:@"" intoString:&value]);
|
||||
|
||||
if (NO == success) {
|
||||
DLog(@"Could not scan header: %@", line);
|
||||
continue;
|
||||
}
|
||||
|
||||
[_responseHeaders setObject:value forKey:[key lowercaseString]];
|
||||
}
|
||||
|
||||
if (200 == statusCode || 206 == statusCode) { // OK
|
||||
return YES;
|
||||
}
|
||||
else if (301 == statusCode || 302 == statusCode) { // Redirect
|
||||
// Handle relative redirects as well
|
||||
NSURL *redirectURL = [[NSURL alloc] initWithString:[self valueForResponseHeader:@"Location"] relativeToURL:[self URL]];
|
||||
[self setURL:[redirectURL absoluteURL]];
|
||||
[self close];
|
||||
return [self connect];
|
||||
}
|
||||
|
||||
DLog(@"Returned status: %li", (long)statusCode);
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)_sendRequest
|
||||
{
|
||||
NSURL *url = [self URL];
|
||||
|
||||
NSString *path;
|
||||
NSString *host;
|
||||
NSNumber *port;
|
||||
|
||||
if (NSClassFromString(@"NSURLComponents")) {
|
||||
// Resolves trailing slash issue, but requires 10.9+
|
||||
|
||||
NSURLComponents * urlComponents = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:YES];
|
||||
|
||||
path = urlComponents.path;
|
||||
host = urlComponents.host;
|
||||
port = urlComponents.port;
|
||||
}
|
||||
else {
|
||||
path = [url path];
|
||||
host = [url host];
|
||||
port = [url port];
|
||||
}
|
||||
|
||||
if (nil == path || [path isEqualToString:@""]) {
|
||||
path = @"/";
|
||||
}
|
||||
|
||||
// The initial GET
|
||||
NSMutableString *requestString = [[NSMutableString alloc] initWithFormat:@"GET %@ HTTP/1.0\r\n", path];
|
||||
|
||||
// Make sure there is a Host entry
|
||||
if (nil != port) {
|
||||
host = [NSString stringWithFormat:@"%@:%@", host, port];
|
||||
}
|
||||
|
||||
[self setValue:host forRequestHeader:@"Host"];
|
||||
|
||||
// Add the request headers
|
||||
for (id header in _requestHeaders) {
|
||||
id value = [_requestHeaders objectForKey:header];
|
||||
|
||||
[requestString appendFormat:@"%@: %@\r\n", header, value];
|
||||
}
|
||||
|
||||
// The final newline
|
||||
[requestString appendString:@"\r\n"];
|
||||
|
||||
// Get the bytes out of it
|
||||
const char *requestBytes = [requestString UTF8String];
|
||||
long requestLength = strlen(requestBytes);
|
||||
|
||||
// Send it off!
|
||||
NSInteger sent = [_socket send:requestBytes amount:requestLength];
|
||||
if (sent != requestLength) {
|
||||
return NO;
|
||||
}
|
||||
DLog(@"Sent:\n%@\n", requestString);
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
// Returns YES for success, NO for...not success
|
||||
- (BOOL)connect
|
||||
{
|
||||
NSURL *url = [self URL];
|
||||
NSString *host;
|
||||
NSNumber *portNumber;
|
||||
|
||||
if (NSClassFromString(@"NSURLComponents")) {
|
||||
NSURLComponents * urlComponents = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:YES];
|
||||
|
||||
host = urlComponents.host;
|
||||
portNumber = urlComponents.port;
|
||||
}
|
||||
else {
|
||||
host = [url host];
|
||||
portNumber = [url port];
|
||||
}
|
||||
|
||||
// Get the port number
|
||||
int port = (int) [portNumber integerValue];
|
||||
if (portNumber == nil) {
|
||||
port = 80; // Default for HTTP
|
||||
}
|
||||
|
||||
// Cogundo...Makin' sockets
|
||||
_socket = [[Socket alloc] initWithHost:host port:port];
|
||||
if (nil == _socket) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (![self _sendRequest]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (![self _readResponse]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)close
|
||||
{
|
||||
[_socket close];
|
||||
_socket = nil;
|
||||
|
||||
if (NULL != _buffer) {
|
||||
free(_buffer);
|
||||
_buffer = NULL;
|
||||
_bufferSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns negative on timeout or error
|
||||
|
||||
- (NSInteger)receiveData:(void *)bytes amount:(NSInteger)amount
|
||||
{
|
||||
NSInteger amountToRequest = amount;
|
||||
NSInteger amountToCopy = MIN(amount, _bufferSize);
|
||||
if (NULL != _buffer) {
|
||||
memcpy(bytes, _buffer, amountToCopy);
|
||||
_bufferSize -= amountToCopy;
|
||||
|
||||
amountToRequest -= amountToCopy;
|
||||
}
|
||||
|
||||
|
||||
NSInteger bytesReceived = amountToCopy + [_socket receive:((uint8_t *)bytes) + amountToCopy amount:amountToRequest];
|
||||
|
||||
return bytesReceived;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,23 +0,0 @@
|
|||
//
|
||||
// Socket.h
|
||||
// Cog
|
||||
//
|
||||
// Created by Vincent Spader on 2/28/07.
|
||||
// Copyright 2007 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
//Rediculously simple socket wrapper
|
||||
@interface Socket : NSObject {
|
||||
int _socket;
|
||||
}
|
||||
|
||||
+ (id)socketWithHost:(NSString *)host port:(int) port;
|
||||
- (id)initWithHost:(NSString *)host port:(int)port;
|
||||
|
||||
- (NSInteger)send:(const void *)data amount:(NSInteger)amount;
|
||||
- (NSInteger)receive:(void *)data amount:(NSInteger)amount;
|
||||
- (void)close;
|
||||
|
||||
@end
|
|
@ -1,76 +0,0 @@
|
|||
//
|
||||
// Socket.m
|
||||
// Cog
|
||||
//
|
||||
// Created by Vincent Spader on 2/28/07.
|
||||
// Copyright 2007 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Socket.h"
|
||||
#import <netdb.h>
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
@implementation Socket
|
||||
|
||||
+ (id)socketWithHost:(NSString *)host port:(int)port
|
||||
{
|
||||
return [[Socket alloc] initWithHost:host port:port];
|
||||
}
|
||||
|
||||
- (id)initWithHost:(NSString *)host port:(int) port
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
struct sockaddr_in sin;
|
||||
struct hostent *he;
|
||||
|
||||
if (_socket < 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(port);
|
||||
|
||||
he = gethostbyname([host UTF8String]);
|
||||
if (!he) {
|
||||
ALog(@"Socket error: %s\n", strerror(errno));
|
||||
close(_socket);
|
||||
return nil;
|
||||
}
|
||||
memcpy(&sin.sin_addr, he->h_addr, 4);
|
||||
|
||||
if (connect(_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
|
||||
ALog(@"Error: %s\n", strerror(errno));
|
||||
close(_socket);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (NSInteger)send:(const void *)data amount:(NSInteger)amount
|
||||
{
|
||||
return send(_socket, data, amount, 0);
|
||||
}
|
||||
|
||||
- (NSInteger)receive:(void *)data amount:(NSInteger)amount
|
||||
{
|
||||
return recv(_socket, data, amount, 0);
|
||||
}
|
||||
|
||||
- (void)close
|
||||
{
|
||||
close(_socket);
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self close];
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in New Issue