cog/Plugins/HTTPSource/HTTPSource.m

295 lines
6.8 KiB
Matlab
Raw Normal View History

2009-03-05 08:01:36 +00:00
//
// HTTPSource.m
// HTTPSource
//
// Created by Vincent Spader on 3/1/07.
// Replaced by Christopher Snowhill on 3/7/20.
2022-01-03 07:34:29 +00:00
// Copyright 2020-2022 __LoSnoCo__. All rights reserved.
2009-03-05 08:01:36 +00:00
//
#import "HTTPSource.h"
#import "Logging.h"
#import <stdlib.h>
#import <string.h>
#define BUFFER_SIZE 262144
2009-03-05 08:01:36 +00:00
@implementation HTTPSource
- (NSURLSession *)createSession {
queue = [[NSOperationQueue alloc] init];
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(!task) return;
if(didReceiveRandomData) {
// Parse ICY header here?
// XXX
didReceiveRandomData = NO;
const char *header = "ICY 200 OK\r\n";
size_t length = [data length];
if(length >= strlen(header)) {
const char *dataBytes = (const char *)[data bytes];
const char *dataStart = dataBytes;
if(memcmp(dataBytes, header, strlen(header)) == 0) {
const char *dataEnd = dataBytes + length;
Boolean endFound = NO;
while(dataBytes + 4 <= dataEnd) {
if(memcmp(dataBytes, "\r\n\r\n", 4) == 0) {
endFound = YES;
break;
}
dataBytes++;
}
if(!endFound) {
@synchronized(task) {
didComplete = YES;
[task cancel];
task = nil;
return;
}
}
dataEnd = dataBytes + 4;
NSUInteger dataLeft = length - (dataEnd - dataStart);
dataBytes = dataStart;
dataBytes += strlen("ICY 200 OK\r\n");
char headerBuffer[80 * 1024 + 1];
while(dataBytes < dataEnd - 2) {
const char *string = dataBytes;
while(dataBytes < dataEnd - 2) {
if(memcmp(dataBytes, "\r\n", 2) == 0) break;
dataBytes++;
}
if(dataBytes - string > 80 * 1024)
dataBytes = string + 80 * 1024;
strncpy(headerBuffer, string, dataBytes - string);
headerBuffer[dataBytes - string] = '\0';
char *colon = strchr(headerBuffer, ':');
if(colon) {
*colon = '\0';
colon++;
}
if(strcasecmp(headerBuffer, "content-type") == 0) {
_mimeType = [NSString stringWithUTF8String:colon];
}
dataBytes += 2;
}
data = [NSData dataWithBytes:dataEnd length:dataLeft];
didReceiveResponse = YES;
}
}
}
@synchronized(bufferedData) {
[bufferedData appendData:data];
_bytesBuffered += [data length];
bytesBuffered = _bytesBuffered;
}
if(bytesBuffered >= BUFFER_SIZE) {
[task suspend];
taskSuspended = YES;
}
}
2009-03-05 08:01:36 +00:00
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
if(statusCode != 200) {
if([response isKindOfClass:[NSHTTPURLResponse class]]) {
completionHandler(NSURLSessionResponseCancel);
@synchronized(task) {
task = nil;
}
return;
}
}
_mimeType = [response MIMEType];
if([_mimeType isEqualToString:@"application/octet-stream"] ||
[_mimeType isEqualToString:@"text/plain"])
didReceiveRandomData = YES;
else
didReceiveResponse = YES;
completionHandler(NSURLSessionResponseAllow);
}
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
newRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLRequest *))completionHandler {
NSURL *url = [request URL];
if([redirectURLs containsObject:url]) {
completionHandler(nil);
@synchronized(self->task) {
self->task = nil;
}
} else {
[redirectURLs addObject:url];
redirected = YES;
didReceiveResponse = NO;
didComplete = NO;
@synchronized(bufferedData) {
[bufferedData setLength:0];
_bytesBuffered = 0;
}
completionHandler(request);
}
}
- (void)URLSession:(NSURLSession *)session
didBecomeInvalidWithError:(NSError *)error {
@synchronized(task) {
task = nil;
}
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
willCacheResponse:(NSCachedURLResponse *)proposedResponse
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {
didComplete = YES;
completionHandler(nil);
}
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error {
@synchronized(self->task) {
self->task = nil;
}
}
- (BOOL)open:(NSURL *)url {
didReceiveResponse = NO;
didReceiveRandomData = NO;
redirected = NO;
taskSuspended = NO;
redirectURLs = [[NSMutableArray alloc] init];
bufferedData = [[NSMutableData alloc] init];
URL = url;
[redirectURLs addObject:URL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
session = [self createSession];
task = [session dataTaskWithRequest:request];
[task resume];
while(task && !didReceiveResponse)
usleep(1000);
if(!task && !didReceiveResponse) return NO;
2009-03-05 08:01:36 +00:00
return YES;
}
- (NSString *)mimeType {
DLog(@"Returning mimetype! %@", _mimeType);
2009-03-05 08:01:36 +00:00
return _mimeType;
}
- (BOOL)seekable {
2009-03-05 08:01:36 +00:00
return NO;
}
- (BOOL)seek:(long)position whence:(int)whence {
2009-03-05 08:01:36 +00:00
return NO;
}
- (long)tell {
2009-03-05 08:01:36 +00:00
return _byteCount;
}
- (long)read:(void *)buffer amount:(long)amount {
@synchronized(bufferedData) {
if(didComplete && ![bufferedData length])
return 0;
}
long totalRead = 0;
long bytesBuffered = 0;
while(totalRead < amount) {
NSData *dataBlock = nil;
NSUInteger copySize = amount - totalRead;
@synchronized(bufferedData) {
if([bufferedData length]) {
if(copySize > [bufferedData length])
copySize = [bufferedData length];
dataBlock = [bufferedData subdataWithRange:NSMakeRange(0, copySize)];
}
}
if(!dataBlock) {
@synchronized(task) {
if(!task || didComplete) return totalRead;
}
usleep(1000);
continue;
}
NSInteger amountReceived = [dataBlock length];
if(amountReceived <= 0) {
break;
}
const void *dataBytes = [dataBlock bytes];
memcpy(((uint8_t *)buffer) + totalRead, dataBytes, amountReceived);
@synchronized(bufferedData) {
[bufferedData replaceBytesInRange:NSMakeRange(0, amountReceived) withBytes:NULL length:0];
_bytesBuffered -= amountReceived;
bytesBuffered = _bytesBuffered;
}
if(!didComplete && taskSuspended && bytesBuffered <= (BUFFER_SIZE * 3 / 4)) {
[task resume];
taskSuspended = NO;
}
totalRead += amountReceived;
2009-03-05 08:01:36 +00:00
}
2009-03-05 08:01:36 +00:00
_byteCount += totalRead;
return totalRead;
}
- (void)close {
if(task) [task cancel];
task = nil;
2009-03-05 08:01:36 +00:00
_mimeType = nil;
}
- (void)dealloc {
2009-03-05 08:01:36 +00:00
[self close];
}
- (NSURL *)url {
return URL;
2009-03-05 08:01:36 +00:00
}
+ (NSArray *)schemes {
return @[@"http", @"https"];
2009-03-05 08:01:36 +00:00
}
@end