diff options
author | Alex Zolotarev <deathbaba@gmail.com> | 2011-11-08 00:03:58 +0400 |
---|---|---|
committer | Alex Zolotarev <alex@maps.me> | 2015-09-23 01:27:29 +0300 |
commit | cf4d77fa0bae136c90581650e4c72b0267baa0a2 (patch) | |
tree | 1164886744cec4a8b83266705475c0e603ee6100 /platform/http_thread_apple.mm | |
parent | 4ca29d9593b4890441dc163999302a870b76162a (diff) |
[mac][downloader] Finished basic chunks support
Diffstat (limited to 'platform/http_thread_apple.mm')
-rw-r--r-- | platform/http_thread_apple.mm | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/platform/http_thread_apple.mm b/platform/http_thread_apple.mm new file mode 100644 index 0000000000..fabe9d655f --- /dev/null +++ b/platform/http_thread_apple.mm @@ -0,0 +1,182 @@ +#import "http_thread_apple.h" + +#include "http_thread_callback.hpp" +#include "platform.hpp" + +#include "../base/logging.hpp" + +#define TIMEOUT_IN_SECONDS 15.0 + +@implementation HttpThread + +- (void) dealloc +{ + LOG(LDEBUG, ("ID:", [self hash], "Connection is destroyed")); + [m_connection cancel]; + [m_connection release]; + [super dealloc]; +} + +- (void) cancel +{ + [m_connection cancel]; +} + +- (id) initWith:(string const &)url callback:(downloader::IHttpThreadCallback &)cb begRange:(int64_t)beg + endRange:(int64_t)end expectedSize:(int64_t)size postBody:(string const &)pb +{ + self = [super init]; + + m_callback = &cb; + m_begRange = beg; + m_endRange = end; + m_downloadedBytes = 0; + m_expectedSize = size; + + NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL: + [NSURL URLWithString:[NSString stringWithUTF8String:url.c_str()]] + cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:TIMEOUT_IN_SECONDS]; + + // use Range header only if we don't download whole file from start + if (!(beg == 0 && end < 0)) + { + NSString * val; + if (end > 0) + { + LOG(LDEBUG, (url, "downloading range [", beg, ",", end, "]")); + val = [[NSString alloc] initWithFormat: @"bytes=%qi-%qi", beg, end]; + } + else + { + LOG(LDEBUG, (url, "resuming download from position", beg)); + val = [[NSString alloc] initWithFormat: @"bytes=%qi-", beg]; + } + [request addValue:val forHTTPHeaderField:@"Range"]; + [val release]; + } + + if (!pb.empty()) + { + NSData * postData = [NSData dataWithBytes:pb.data() length:pb.size()]; + [request setHTTPBody:postData]; + [request setHTTPMethod:@"POST"]; + [request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + } + // set user-agent with unique client id only for mapswithme requests + if (url.find("mapswithme.com") != string::npos) + { + static string const uid = GetPlatform().UniqueClientId(); + [request addValue:[NSString stringWithUTF8String: uid.c_str()] forHTTPHeaderField:@"User-Agent"]; + } + + // create the connection with the request and start loading the data + m_connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; + + if (m_connection == 0) + { + LOG(LERROR, ("Can't create connection for", url)); + [self release]; + return nil; + } + else + LOG(LDEBUG, ("ID:", [self hash], "Starting connection to", url)); + + return self; +} + +/// @return -1 if can't decode ++ (int64_t) getContentRange:(NSDictionary *)httpHeader +{ + NSString * cr = [httpHeader valueForKey:@"Content-Range"]; + if (cr) + { + NSArray * arr = [cr componentsSeparatedByString:@"/"]; + if (arr && [arr count]) + return [(NSString *)[arr objectAtIndex:[arr count] - 1] longLongValue]; + } + return -1; +} + +- (void) connection: (NSURLConnection *)connection didReceiveResponse: (NSURLResponse *)response +{ + // This method is called when the server has determined that it + // has enough information to create the NSURLResponse. + + // check if this is OK (not a 404 or the like) + if ([response isKindOfClass:[NSHTTPURLResponse class]]) + { + NSInteger const statusCode = [(NSHTTPURLResponse *)response statusCode]; + LOG(LDEBUG, ("Got response with status code", statusCode)); + if (statusCode < 200 || statusCode > 299) + { + LOG(LWARNING, ("Received HTTP error, canceling download", statusCode)); + [m_connection cancel]; + m_callback->OnFinish(statusCode, m_begRange, m_endRange); + return; + } + else if (m_expectedSize > 0) + { + // get full file expected size from Content-Range header + int64_t sizeOnServer = [HttpThread getContentRange:[(NSHTTPURLResponse *)response allHeaderFields]]; + // if it's absent, use Content-Length instead + if (sizeOnServer < 0) + sizeOnServer = [response expectedContentLength]; + if (sizeOnServer > 0 & m_expectedSize != sizeOnServer) + { + + LOG(LWARNING, ("Canceling download - server replied with invalid size", + sizeOnServer, "!=", m_expectedSize)); + [m_connection cancel]; + m_callback->OnFinish(-2, m_begRange, m_endRange); + return; + } + } + } + else + { // in theory, we should never be here + LOG(LWARNING, ("Invalid non-http response, aborting request")); + [m_connection cancel]; + m_callback->OnFinish(-1, m_begRange, m_endRange); + } +} + +- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + int64_t const length = [data length]; + m_downloadedBytes += length; + m_callback->OnWrite(m_begRange + m_downloadedBytes - length, [data bytes], length); +} + +- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + LOG(LWARNING, ("Connection failed", [[error localizedDescription] cStringUsingEncoding:NSUTF8StringEncoding])); + m_callback->OnFinish([error code], m_begRange, m_endRange); +} + +- (void) connectionDidFinishLoading:(NSURLConnection *)connection +{ + m_callback->OnFinish(200, m_begRange, m_endRange); +} + +@end + +/////////////////////////////////////////////////////////////////////////////////////// +namespace downloader +{ +HttpThread * CreateNativeHttpThread(string const & url, + downloader::IHttpThreadCallback & cb, + int64_t beg, + int64_t end, + int64_t size, + string const & pb) +{ + return [[HttpThread alloc] initWith:url callback:cb begRange:beg endRange:end expectedSize:size postBody:pb]; +} + +void DeleteNativeHttpThread(HttpThread * request) +{ + [request cancel]; + [request release]; +} + +} // namespace downloader |