Brought back custom socket class. Added HTTPConnection class to handle blocking HTTP connections.
parent
bf7b2c0a2b
commit
870620c2b8
|
@ -8,21 +8,19 @@
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
#import <JNetLib/jnetlib.h>
|
|
||||||
|
|
||||||
#import "Plugin.h"
|
#import "Plugin.h"
|
||||||
|
|
||||||
|
@class HTTPConnection;
|
||||||
|
|
||||||
@interface HTTPSource : NSObject <CogSource>
|
@interface HTTPSource : NSObject <CogSource>
|
||||||
{
|
{
|
||||||
NSURLConnection *_connection;
|
HTTPConnection *_connection;
|
||||||
|
|
||||||
JNL_HTTPGet *_get;
|
|
||||||
|
|
||||||
long _byteCount;
|
long _byteCount;
|
||||||
|
|
||||||
NSString *_mimeType;
|
NSString *_mimeType;
|
||||||
|
|
||||||
NSURL *_url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -7,45 +7,25 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "HTTPSource.h"
|
#import "HTTPSource.h"
|
||||||
#include <JNetLib/jnetlib.h>
|
#import "HTTPConnection.h"
|
||||||
|
|
||||||
@implementation HTTPSource
|
@implementation HTTPSource
|
||||||
|
|
||||||
- (BOOL)open:(NSURL *)url
|
- (BOOL)open:(NSURL *)url
|
||||||
{
|
{
|
||||||
_url = [url copy];
|
_connection = [[HTTPConnection alloc] initWithURL:url];
|
||||||
|
|
||||||
JNL::open_socketlib();
|
NSString *userAgent = [NSString stringWithFormat:@"Cog %@", [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey]];
|
||||||
|
[_connection setValue:userAgent forRequestHeader:@"User-Agent"];
|
||||||
_get = new JNL_HTTPGet();
|
[_connection setValue:@"close" forRequestHeader:@"Connection"];
|
||||||
|
[_connection setValue:@"*/*" forRequestHeader:@"Accept"];
|
||||||
NSString *userAgent = [NSString stringWithFormat:@"User-Agent:Cog %@", [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey]];
|
|
||||||
_get->addheader([userAgent UTF8String]);
|
BOOL success = [_connection connect];
|
||||||
_get->addheader("Connection:close");
|
if (NO == success) {
|
||||||
_get->addheader("Accept:*/*");
|
|
||||||
|
|
||||||
_get->connect([[url absoluteString] UTF8String]);
|
|
||||||
for(;;) {
|
|
||||||
int status = _get->get_status();
|
|
||||||
|
|
||||||
if (status != 0 && status != 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_get->run() < 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int st = _get->run();
|
|
||||||
if (st < 0) {
|
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *mimeType = _get->getheader("content-type");
|
_mimeType = [[_connection valueForResponseHeader:@"Content-type"] copy];
|
||||||
if (NULL != mimeType) {
|
|
||||||
_mimeType = [[NSString alloc] initWithUTF8String:mimeType];
|
|
||||||
}
|
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
@ -75,36 +55,13 @@
|
||||||
{
|
{
|
||||||
int totalRead = 0;
|
int totalRead = 0;
|
||||||
|
|
||||||
NSTimeInterval timeout = 30;
|
|
||||||
BOOL timingOut = NO;
|
|
||||||
|
|
||||||
while (totalRead < amount) {
|
while (totalRead < amount) {
|
||||||
int result = _get->run();
|
NSInteger amountReceived = [_connection receiveData:((uint8_t *)buffer) + totalRead amount:amount - totalRead];
|
||||||
int amountRead = _get->get_bytes((char *)((uint8_t *)buffer) + totalRead, amount - totalRead);
|
if (amountReceived <= 0) {
|
||||||
|
break;
|
||||||
if (0 == amountRead) {
|
|
||||||
if (0 != result) break;
|
|
||||||
|
|
||||||
if (NO == timingOut) {
|
|
||||||
timingOut = YES;
|
|
||||||
timeout = [NSDate timeIntervalSinceReferenceDate] + 30;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (timeout < [NSDate timeIntervalSinceReferenceDate]) {
|
|
||||||
NSLog(@"Timed out!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sigh. We should just use blocking IO.
|
|
||||||
usleep(250);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
timingOut = NO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
totalRead += amountRead;
|
totalRead += amountReceived;
|
||||||
}
|
}
|
||||||
|
|
||||||
_byteCount += totalRead;
|
_byteCount += totalRead;
|
||||||
|
@ -114,13 +71,9 @@
|
||||||
|
|
||||||
- (void)close
|
- (void)close
|
||||||
{
|
{
|
||||||
if (NULL != _get) {
|
[_connection close];
|
||||||
delete _get;
|
[_connection release];
|
||||||
_get = NULL;
|
_connection = nil;
|
||||||
}
|
|
||||||
|
|
||||||
[_url release];
|
|
||||||
_url = nil;
|
|
||||||
|
|
||||||
[_mimeType release];
|
[_mimeType release];
|
||||||
_mimeType = nil;
|
_mimeType = nil;
|
||||||
|
@ -136,7 +89,7 @@
|
||||||
|
|
||||||
- (NSURL *)url
|
- (NSURL *)url
|
||||||
{
|
{
|
||||||
return _url;
|
return [_connection URL];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (NSArray *)schemes
|
+ (NSArray *)schemes
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* 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 */; };
|
||||||
17ADB6100B97A74800257CA2 /* HTTPSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 17ADB60D0B97A74800257CA2 /* HTTPSource.mm */; };
|
17ADB6100B97A74800257CA2 /* HTTPSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 17ADB60D0B97A74800257CA2 /* HTTPSource.mm */; };
|
||||||
17F6C6540F5F9E83000D9DA9 /* JNetLib.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17F6C6450F5F9E3F000D9DA9 /* JNetLib.framework */; };
|
17F6C6540F5F9E83000D9DA9 /* JNetLib.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17F6C6450F5F9E3F000D9DA9 /* JNetLib.framework */; };
|
||||||
17F6C6550F5F9E86000D9DA9 /* JNetLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17F6C6450F5F9E3F000D9DA9 /* JNetLib.framework */; };
|
17F6C6550F5F9E86000D9DA9 /* JNetLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17F6C6450F5F9E3F000D9DA9 /* JNetLib.framework */; };
|
||||||
|
@ -47,6 +49,10 @@
|
||||||
089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
||||||
17ADB60C0B97A74800257CA2 /* HTTPSource.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = HTTPSource.h; sourceTree = "<group>"; };
|
17ADB60C0B97A74800257CA2 /* HTTPSource.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = HTTPSource.h; sourceTree = "<group>"; };
|
||||||
17ADB60D0B97A74800257CA2 /* HTTPSource.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; path = HTTPSource.mm; sourceTree = "<group>"; };
|
17ADB60D0B97A74800257CA2 /* HTTPSource.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; path = HTTPSource.mm; sourceTree = "<group>"; };
|
||||||
17ADB6340B97A8B400257CA2 /* Plugin.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Plugin.h; path = ../../Audio/Plugin.h; sourceTree = SOURCE_ROOT; };
|
17ADB6340B97A8B400257CA2 /* Plugin.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Plugin.h; path = ../../Audio/Plugin.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
@ -74,6 +80,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
08FB77AFFE84173DC02AAC07 /* Classes */,
|
08FB77AFFE84173DC02AAC07 /* Classes */,
|
||||||
|
1716067A0F624E09008FA424 /* Utils */,
|
||||||
32C88E010371C26100C91783 /* Other Sources */,
|
32C88E010371C26100C91783 /* Other Sources */,
|
||||||
089C167CFE841241C02AAC07 /* Resources */,
|
089C167CFE841241C02AAC07 /* Resources */,
|
||||||
089C1671FE841209C02AAC07 /* Frameworks and Libraries */,
|
089C1671FE841209C02AAC07 /* Frameworks and Libraries */,
|
||||||
|
@ -128,6 +135,17 @@
|
||||||
name = "Other Frameworks";
|
name = "Other Frameworks";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
1716067A0F624E09008FA424 /* Utils */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
171606860F624E27008FA424 /* HTTPConnection.h */,
|
||||||
|
171606870F624E27008FA424 /* HTTPConnection.m */,
|
||||||
|
1716067B0F624E09008FA424 /* Socket.h */,
|
||||||
|
1716067C0F624E09008FA424 /* Socket.m */,
|
||||||
|
);
|
||||||
|
path = Utils;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
17F6C6410F5F9E3F000D9DA9 /* Products */ = {
|
17F6C6410F5F9E3F000D9DA9 /* Products */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -224,6 +242,8 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
17ADB6100B97A74800257CA2 /* HTTPSource.mm in Sources */,
|
17ADB6100B97A74800257CA2 /* HTTPSource.mm in Sources */,
|
||||||
|
1716067D0F624E09008FA424 /* Socket.m in Sources */,
|
||||||
|
1716068A0F624E27008FA424 /* HTTPConnection.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -282,7 +302,7 @@
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
PREBINDING = NO;
|
PREBINDING = NO;
|
||||||
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
|
SDKROOT = /Developer/SDKs/MacOSX10.5.sdk;
|
||||||
SYMROOT = ../../build;
|
SYMROOT = ../../build;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
|
@ -293,7 +313,7 @@
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
PREBINDING = NO;
|
PREBINDING = NO;
|
||||||
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
|
SDKROOT = /Developer/SDKs/MacOSX10.5.sdk;
|
||||||
SYMROOT = ../../build;
|
SYMROOT = ../../build;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
//
|
||||||
|
// 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
|
|
@ -0,0 +1,273 @@
|
||||||
|
//
|
||||||
|
// HTTPConnection.m
|
||||||
|
// HTTPSource
|
||||||
|
//
|
||||||
|
// Created by Vincent Spader on 3/6/09.
|
||||||
|
// Copyright 2009 __MyCompanyName__. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "HTTPConnection.h"
|
||||||
|
|
||||||
|
#include <netdb.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
|
||||||
|
{
|
||||||
|
[_requestHeaders release];
|
||||||
|
[_responseHeaders release];
|
||||||
|
|
||||||
|
[self setURL:nil];
|
||||||
|
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (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);
|
||||||
|
NSString *line = [[NSString alloc] initWithBytes:_buffer length:lineLength encoding:NSUTF8StringEncoding];
|
||||||
|
NSLog(@"Received line: !%@!", line);
|
||||||
|
memmove(_buffer, _buffer + lineLength + 2, _bufferSize - lineLength); // + 2 to skip the newline!
|
||||||
|
_bufferSize -= lineLength;
|
||||||
|
|
||||||
|
return [line autorelease];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)_readResponse
|
||||||
|
{
|
||||||
|
// Clear out any old response headers
|
||||||
|
[_responseHeaders release];
|
||||||
|
_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]);
|
||||||
|
[scanner release];
|
||||||
|
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!
|
||||||
|
NSLog(@"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]);
|
||||||
|
[scanner release];
|
||||||
|
|
||||||
|
if (NO == success) {
|
||||||
|
NSLog(@"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
|
||||||
|
NSURL *redirectURL = [[NSURL alloc] initWithString:[self valueForResponseHeader:@"Location"]];
|
||||||
|
[self setURL:redirectURL];
|
||||||
|
[self close];
|
||||||
|
return [self connect];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSLog(@"Returned status: %i", statusCode);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)_sendRequest
|
||||||
|
{
|
||||||
|
NSURL *url = [self URL];
|
||||||
|
|
||||||
|
// The initial GET
|
||||||
|
NSMutableString *requestString = [[NSMutableString alloc] initWithFormat:@"GET %@ HTTP/1.0\r\n", [url path]];
|
||||||
|
|
||||||
|
// Make sure there is a Host entry
|
||||||
|
NSString *host = [url host];
|
||||||
|
if (nil != [url port]) {
|
||||||
|
host = [NSString stringWithFormat:@"%@:%@", [url host], [url 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];
|
||||||
|
int requestLength = strlen(requestBytes);
|
||||||
|
|
||||||
|
// Send it off!
|
||||||
|
NSInteger sent = [_socket send:requestBytes amount:requestLength];
|
||||||
|
if (sent != requestLength) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
NSLog(@"Sent:\n%@\n", requestString);
|
||||||
|
[requestString release];
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns YES for success, NO for...not success
|
||||||
|
- (BOOL)connect
|
||||||
|
{
|
||||||
|
NSURL *url = [self URL];
|
||||||
|
NSString *host = [url host];
|
||||||
|
|
||||||
|
// Get the port number
|
||||||
|
NSNumber *portNumber = [url port];
|
||||||
|
NSInteger port = [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 release];
|
||||||
|
_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
|
|
@ -0,0 +1,23 @@
|
||||||
|
//
|
||||||
|
// 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
|
|
@ -0,0 +1,76 @@
|
||||||
|
//
|
||||||
|
// Socket.m
|
||||||
|
// Cog
|
||||||
|
//
|
||||||
|
// Created by Vincent Spader on 2/28/07.
|
||||||
|
// Copyright 2007 __MyCompanyName__. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "Socket.h"
|
||||||
|
#import <netdb.h>
|
||||||
|
|
||||||
|
@implementation Socket
|
||||||
|
|
||||||
|
+ (id)socketWithHost:(NSString *)host port:(NSInteger)port
|
||||||
|
{
|
||||||
|
return [[[Socket alloc] initWithHost:host port:port] autorelease];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)initWithHost:(NSString *)host port:(NSInteger) 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) {
|
||||||
|
NSLog(@"Socket error.");
|
||||||
|
close(_socket);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
memcpy(&sin.sin_addr, he->h_addr, 4);
|
||||||
|
|
||||||
|
if (connect(_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
|
||||||
|
NSLog(@"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];
|
||||||
|
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
Loading…
Reference in New Issue