API  0.9.7
 All Classes Files Functions Variables Macros Groups Pages
CPURLConnection.j
Go to the documentation of this file.
1 /*
2  * CPURLConnection.j
3  * Foundation
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
25 
70 @implementation CPURLConnection : CPObject
71 {
72  CPURLRequest _request;
73  id _delegate;
74  BOOL _isCanceled;
75  BOOL _isLocalFileConnection;
76 
77  HTTPRequest _HTTPRequest;
78 }
79 
80 + (void)setClassDelegate:(id)delegate
81 {
82  CPURLConnectionDelegate = delegate;
83 }
84 
85 /*
86  Sends a request for the data from a URL. This is the easiest way to obtain data from a URL.
87  @param aRequest contains the URL to request the data from
88  @param aURLResponse not used
89  @param anError not used
90  @return the data at the URL or \c nil if there was an error
91 */
92 + (CPData)sendSynchronousRequest:(CPURLRequest)aRequest returningResponse:(/*{*/CPURLResponse/*}*/)aURLResponse
93 {
94  try
95  {
96  var request = new CFHTTPRequest();
97 
98  request.open([aRequest HTTPMethod], [[aRequest URL] absoluteString], NO);
99 
100  var fields = [aRequest allHTTPHeaderFields],
101  key = nil,
102  keys = [fields keyEnumerator];
103 
104  while ((key = [keys nextObject]) !== nil)
105  request.setRequestHeader(key, [fields objectForKey:key]);
106 
107  request.send([aRequest HTTPBody]);
108 
109  if (!request.success())
110  return nil;
111 
112  return [CPData dataWithRawString:request.responseText()];
113  }
114  catch (anException)
115  {
116  }
117 
118  return nil;
119 }
120 
121 /*
122  Creates a url connection with a delegate to monitor the request progress.
123  @param aRequest contains the URL to obtain data from
124  @param aDelegate will be sent messages related to the request progress
125  @return a connection that can be \c started to initiate the request
126 */
127 + (CPURLConnection)connectionWithRequest:(CPURLRequest)aRequest delegate:(id)aDelegate
128 {
129  return [[self alloc] initWithRequest:aRequest delegate:aDelegate];
130 }
131 
132 /*
133  Default class initializer. Use one of the class methods instead.
134  @param aRequest contains the URL to contact
135  @param aDelegate will receive progress messages
136  @param shouldStartImmediately whether the \c -start method should be called from here
137  @return the initialized url connection
138 */
139 - (id)initWithRequest:(CPURLRequest)aRequest delegate:(id)aDelegate startImmediately:(BOOL)shouldStartImmediately
140 {
141  self = [super init];
142 
143  if (self)
144  {
145  _request = aRequest;
146  _delegate = aDelegate;
147  _isCanceled = NO;
148 
149  var URL = [_request URL],
150  scheme = [URL scheme];
151 
152  // Browsers use "file:", Titanium uses "app:"
153  _isLocalFileConnection = scheme === "file" ||
154  ((scheme === "http" || scheme === "https") &&
155  window.location &&
156  (window.location.protocol === "file:" || window.location.protocol === "app:"));
157 
158  _HTTPRequest = new CFHTTPRequest();
159 
160  if (shouldStartImmediately)
161  [self start];
162  }
163 
164  return self;
165 }
166 
167 - (id)initWithRequest:(CPURLRequest)aRequest delegate:(id)aDelegate
168 {
169  return [self initWithRequest:aRequest delegate:aDelegate startImmediately:YES];
170 }
171 
172 /*
173  return the delegate
174 */
175 - (id)delegate
176 {
177  return _delegate;
178 }
179 
180 /*
181  Start the connection. Not needed if you used the class method +connectionWithRequest:delegate:
182 */
183 - (void)start
184 {
185  _isCanceled = NO;
186 
187  try
188  {
189  _HTTPRequest.open([_request HTTPMethod], [[_request URL] absoluteString], YES);
190 
191  _HTTPRequest.onreadystatechange = function() { [self _readyStateDidChange]; };
192 
193  var fields = [_request allHTTPHeaderFields],
194  key = nil,
195  keys = [fields keyEnumerator];
196 
197  while ((key = [keys nextObject]) !== nil)
198  _HTTPRequest.setRequestHeader(key, [fields objectForKey:key]);
199 
200  _HTTPRequest.send([_request HTTPBody]);
201  }
202  catch (anException)
203  {
204  if ([_delegate respondsToSelector:@selector(connection:didFailWithError:)])
205  [_delegate connection:self didFailWithError:anException];
206  }
207 }
208 
209 /*
210  Cancels the current request.
211 */
212 - (void)cancel
213 {
214  _isCanceled = YES;
215 
216  try
217  {
218  _HTTPRequest.abort();
219  }
220  // We expect an exception in some browsers like FireFox.
221  catch (anException)
222  {
223  }
224 }
225 
226 - (BOOL)isLocalFileConnection
227 {
228  return _isLocalFileConnection;
229 }
230 
231 /* @ignore */
232 - (void)_readyStateDidChange
233 {
234  if (_HTTPRequest.readyState() === CFHTTPRequest.CompleteState)
235  {
236  var statusCode = _HTTPRequest.status(),
237  URL = [_request URL];
238 
239  if (statusCode === 401 && [CPURLConnectionDelegate respondsToSelector:@selector(connectionDidReceiveAuthenticationChallenge:)])
240  [CPURLConnectionDelegate connectionDidReceiveAuthenticationChallenge:self];
241  else
242  {
243  if ([_delegate respondsToSelector:@selector(connection:didReceiveResponse:)])
244  {
245  if (_isLocalFileConnection)
246  [_delegate connection:self didReceiveResponse:[[CPURLResponse alloc] initWithURL:URL]];
247  else
248  {
249  var response = [[CPHTTPURLResponse alloc] initWithURL:URL];
250  [response _setStatusCode:statusCode];
251  [response _setAllResponseHeaders:_HTTPRequest.getAllResponseHeaders()];
252  [_delegate connection:self didReceiveResponse:response];
253  }
254  }
255 
256  if (!_isCanceled)
257  {
258  if ([_delegate respondsToSelector:@selector(connection:didReceiveData:)])
259  [_delegate connection:self didReceiveData:_HTTPRequest.responseText()];
260  if ([_delegate respondsToSelector:@selector(connectionDidFinishLoading:)])
261  [_delegate connectionDidFinishLoading:self];
262  }
263  }
264  }
265 
266  [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
267 }
268 
269 /* @ignore */
270 - (HTTPRequest)_HTTPRequest
271 {
272  return _HTTPRequest;
273 }
274 
275 @end
276 
278 
279 + (CPData)sendSynchronousRequest:(CPURLRequest)aRequest returningResponse:(/*{*/CPURLResponse/*}*/)aURLResponse error:(id)anError
280 {
281  _CPReportLenientDeprecation(self, _cmd, @selector(sendSynchronousRequest:returningResponse:));
282 
283  return [self sendSynchronousRequest:aRequest returningResponse:aURLResponse];
284 }
285 
286 - (HTTPRequest)_XMLHTTPRequest
287 {
288  _CPReportLenientDeprecation(self, _cmd, @selector(_HTTPRequest));
289 
290  return [self _HTTPRequest];
291 }
292 
293 @end