1 |
// |
2 |
// BSDownloadTask.m |
3 |
// BathyScaphe |
4 |
// |
5 |
// Created by Hori,Masaki on 06/08/06. |
6 |
// Copyright 2006-2009,2012 BathyScaphe Project. All rights reserved. |
7 |
// encoding="UTF-8" |
8 |
// |
9 |
|
10 |
#import "BSDownloadTask.h" |
11 |
|
12 |
NSString *const BSDownloadTaskFinishDownloadNotification = @"BSDownloadTaskFinishDownloadNotification"; |
13 |
NSString *const BSDownloadTaskReceiveResponseNotification = @"BSDownloadTaskReceiveResponseNotification"; |
14 |
NSString *const BSDownloadTaskCanceledNotification = @"BSDownloadTaskCanceledNotification"; |
15 |
NSString *const BSDownloadTaskInternalErrorNotification = @"BSDownloadTaskInternalErrorNotification"; |
16 |
NSString *const BSDownloadTaskAbortDownloadNotification = @"BSDownloadTaskAbortDownloadNotification"; |
17 |
NSString *const BSDownloadTaskServerResponseKey = @"BSDownloadTaskServerResponseKey"; // NSURLResponse |
18 |
NSString *const BSDownloadTaskStatusCodeKey = @"BSDownloadTaskStatusCodeKey"; // NSNumber (int) |
19 |
NSString *const BSDownloadTaskFailDownloadNotification = @"BSDownloadTaskFailDownloadNotification"; |
20 |
NSString *const BSDownloadTaskErrorObjectKey = @"BSDownloadTaskErrorObjectKey"; // NSError |
21 |
|
22 |
@interface BSDownloadTask () |
23 |
// re-declare override Writability |
24 |
@property (readwrite, copy) NSString *message; |
25 |
|
26 |
@property CGFloat currentLength; |
27 |
@property CGFloat contLength; |
28 |
@property (retain) id response; |
29 |
@end |
30 |
|
31 |
@implementation BSDownloadTask |
32 |
// message property implementation in BSThreadListTask |
33 |
@dynamic message; |
34 |
|
35 |
@synthesize URL = m_targetURL; |
36 |
@synthesize currentLength = m_currentLength; |
37 |
@synthesize contLength = m_contLength; |
38 |
@synthesize response = _response; |
39 |
|
40 |
+ (id)taskWithURL:(NSURL *)url |
41 |
{ |
42 |
return [[[self alloc] initWithURL:url] autorelease]; |
43 |
} |
44 |
|
45 |
- (id)initWithURL:(NSURL *)url |
46 |
{ |
47 |
if(self = [super init]) { |
48 |
self.URL = url; |
49 |
self.isInProgress = YES; |
50 |
m_contLengthIsUnknown = YES; |
51 |
} |
52 |
|
53 |
return self; |
54 |
} |
55 |
|
56 |
+ (id)taskWithURL:(NSURL *)url method:(NSString *)method |
57 |
{ |
58 |
return [[[self alloc] initWithURL:url method:method] autorelease]; |
59 |
} |
60 |
|
61 |
- (id)initWithURL:(NSURL *)url method:(NSString *)inMethod |
62 |
{ |
63 |
if(self = [self initWithURL:url]) { |
64 |
method = [inMethod retain]; |
65 |
} |
66 |
|
67 |
return self; |
68 |
} |
69 |
|
70 |
- (void)dealloc |
71 |
{ |
72 |
[m_targetURL release]; |
73 |
[con release]; |
74 |
[receivedData release]; |
75 |
[method release]; |
76 |
[_response release]; |
77 |
|
78 |
[super dealloc]; |
79 |
} |
80 |
|
81 |
#pragma mark Accessors |
82 |
|
83 |
- (NSData *)receivedData |
84 |
{ |
85 |
return receivedData; |
86 |
} |
87 |
|
88 |
|
89 |
#pragma mark Overrides |
90 |
- (void)excute |
91 |
{ |
92 |
[self synchronousDownLoad]; |
93 |
} |
94 |
- (void)synchronousDownLoad |
95 |
{ |
96 |
NSRunLoop *loop = [NSRunLoop currentRunLoop]; |
97 |
|
98 |
[receivedData release]; |
99 |
receivedData = nil; |
100 |
self.currentLength = 0; |
101 |
self.contLength = 0; |
102 |
self.amount = -1; |
103 |
self.message = [NSString stringWithFormat:NSLocalizedStringFromTable(@"Download url(%@)", @"Downloader", @""), [self.URL absoluteString]]; |
104 |
|
105 |
NSMutableURLRequest *request; |
106 |
|
107 |
request = [NSMutableURLRequest requestWithURL:self.URL]; |
108 |
if (!request) { |
109 |
[self postNotificationWithName:BSDownloadTaskInternalErrorNotification userInfo:nil]; |
110 |
return; |
111 |
} |
112 |
[request setValue:[NSBundle monazillaUserAgent] forHTTPHeaderField:@"User-Agent"]; |
113 |
if (method) { |
114 |
[request setHTTPMethod:method]; |
115 |
} |
116 |
|
117 |
con = [[NSURLConnection alloc] initWithRequest:request delegate:self]; |
118 |
if (!con) { |
119 |
[self postNotificationWithName:BSDownloadTaskInternalErrorNotification userInfo:nil]; |
120 |
return; |
121 |
} |
122 |
|
123 |
while (self.isInProgress) { |
124 |
id pool = [[NSAutoreleasePool alloc] init]; |
125 |
[loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; |
126 |
[pool release]; |
127 |
} |
128 |
} |
129 |
|
130 |
#pragma mark CMRTask |
131 |
- (void)cancel:(id)sender |
132 |
{ |
133 |
[con cancel]; |
134 |
[self postNotificationWithName:BSDownloadTaskCanceledNotification userInfo:nil]; |
135 |
|
136 |
[super cancel:sender]; |
137 |
} |
138 |
|
139 |
- (NSString *)title |
140 |
{ |
141 |
return NSLocalizedStringFromTable(@"Download.", @"Downloader", @""); |
142 |
} |
143 |
|
144 |
@end |
145 |
|
146 |
|
147 |
@implementation BSDownloadTask(NSURLConnectionDelegate) |
148 |
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response; |
149 |
{ |
150 |
// Leopard |
151 |
if (!response) { |
152 |
return request; |
153 |
} |
154 |
self.response = response; |
155 |
[self postNotificaionWithResponse:response]; |
156 |
[connection cancel]; |
157 |
return nil; |
158 |
} |
159 |
|
160 |
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response |
161 |
{ |
162 |
BOOL disconnect = NO; |
163 |
|
164 |
self.response = response; |
165 |
if ([[NSUserDefaults standardUserDefaults] boolForKey:BSUserDebugEnabledKey]) { |
166 |
NSLog(@"** USER DEBUG **\n%@", [(NSHTTPURLResponse *)response allHeaderFields]); |
167 |
} |
168 |
switch ([(NSHTTPURLResponse *)response statusCode]) { |
169 |
case 200: |
170 |
case 206: |
171 |
break; |
172 |
case 304: |
173 |
NSLog(@"Content is not modified."); |
174 |
disconnect = YES; |
175 |
break; |
176 |
case 404: |
177 |
NSLog(@"Content has not found."); |
178 |
disconnect = YES; |
179 |
break; |
180 |
case 416: |
181 |
NSLog(@"Range is mismatch."); |
182 |
disconnect = YES; |
183 |
break; |
184 |
default: |
185 |
NSLog(@"Unknown error."); |
186 |
disconnect = YES; |
187 |
break; |
188 |
} |
189 |
if (disconnect) { |
190 |
[connection cancel]; |
191 |
[self postNotificaionWithResponse:response]; |
192 |
|
193 |
return; |
194 |
} |
195 |
|
196 |
// [self postNotificaionWithResponseDontFinish:response]; // 2009-03-24 ��������������������������������������������������������������� |
197 |
CGFloat length = [response expectedContentLength]; |
198 |
if (length <= 0) { |
199 |
CGFloat assumedLength = [[[(NSHTTPURLResponse *)response allHeaderFields] objectForKey:@"Content-Length"] doubleValue]; |
200 |
if (assumedLength > 0) { |
201 |
self.contLength = assumedLength*1.7; // gzip ������������������������ |
202 |
m_contLengthIsUnknown = NO; |
203 |
} |
204 |
} else { |
205 |
m_contLengthIsUnknown = NO; |
206 |
self.contLength = length; |
207 |
} |
208 |
} |
209 |
|
210 |
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data |
211 |
{ |
212 |
if (!receivedData) { |
213 |
receivedData = [[NSMutableData alloc] init]; |
214 |
} |
215 |
|
216 |
if (!receivedData) { |
217 |
// abort |
218 |
[connection cancel]; |
219 |
[self postNotificationWithName:BSDownloadTaskInternalErrorNotification userInfo:nil]; |
220 |
|
221 |
return; |
222 |
} |
223 |
|
224 |
[receivedData appendData:data]; |
225 |
self.currentLength = [receivedData length]; |
226 |
|
227 |
if (!m_contLengthIsUnknown && (self.contLength > 0)) { |
228 |
CGFloat bar = self.currentLength/self.contLength*100.0; |
229 |
self.amount = bar; |
230 |
self.message = [NSString stringWithFormat:NSLocalizedStringFromTable(@"Download url(%@) (%.0fk of %.0fk)", @"Downloader", @""), |
231 |
[self.URL absoluteString], (CGFloat)self.currentLength/1024, (CGFloat)self.contLength/1024]; |
232 |
} else { |
233 |
self.message = [NSString stringWithFormat:NSLocalizedStringFromTable(@"Download url(%@) (%.0fk)", @"Downloader", @""), |
234 |
[self.URL absoluteString], (CGFloat)self.currentLength/1024]; |
235 |
} |
236 |
} |
237 |
|
238 |
- (void)connectionDidFinishLoading:(NSURLConnection *)connection |
239 |
{ |
240 |
[self postNotificationWithName:BSDownloadTaskFinishDownloadNotification userInfo:nil]; |
241 |
} |
242 |
|
243 |
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error |
244 |
{ |
245 |
// abort |
246 |
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:error forKey:BSDownloadTaskErrorObjectKey]; |
247 |
[self postNotificationWithName:BSDownloadTaskFailDownloadNotification userInfo:userInfo]; |
248 |
self.isInProgress = NO; |
249 |
} |
250 |
@end |
251 |
|
252 |
|
253 |
@implementation BSDownloadTask(TaskNotification) |
254 |
- (void)postNotificationWithName:(NSString *)name userInfo:(NSDictionary *)info |
255 |
{ |
256 |
[[NSNotificationCenter defaultCenter] postNotificationName:name object:self userInfo:info]; |
257 |
self.isInProgress = NO; |
258 |
} |
259 |
|
260 |
- (void)postNotificaionWithResponse:(NSURLResponse *)response |
261 |
{ |
262 |
NSDictionary *info; |
263 |
|
264 |
info = [NSDictionary dictionaryWithObjectsAndKeys:response, BSDownloadTaskServerResponseKey, |
265 |
[NSNumber numberWithInteger:[(NSHTTPURLResponse *)response statusCode]], BSDownloadTaskStatusCodeKey, |
266 |
NULL]; |
267 |
[self postNotificationWithName:BSDownloadTaskAbortDownloadNotification userInfo:info]; |
268 |
} |
269 |
|
270 |
- (void)postNotificaionWithResponseDontFinish:(NSURLResponse *)response |
271 |
{ |
272 |
NSDictionary *info; |
273 |
|
274 |
info = [NSDictionary dictionaryWithObjectsAndKeys:response, BSDownloadTaskServerResponseKey, |
275 |
[NSNumber numberWithInteger:[(NSHTTPURLResponse *)response statusCode]], BSDownloadTaskStatusCodeKey, |
276 |
NULL]; |
277 |
[self postNotificationWithName:BSDownloadTaskReceiveResponseNotification userInfo:info]; |
278 |
} |
279 |
@end |