API  0.9.8
 All Classes Files Functions Variables Typedefs 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 
24 @typedef HTTPRequest
25 
27 
71 @implementation CPURLConnection : CPObject
72 {
73  CPURLRequest _originalRequest;
74  CPURLRequest _request;
75  id _delegate;
76  BOOL _isCanceled;
77  BOOL _isLocalFileConnection;
78 
79  HTTPRequest _HTTPRequest;
80 }
81 
82 + (void)setClassDelegate:(id)delegate
83 {
85 }
86 
87 /*
88  Sends a request for the data from a URL. This is the easiest way to obtain data from a URL.
89  @param aRequest contains the URL to request the data from
90  @param aURLResponse not used
91  @param anError not used
92  @return the data at the URL or \c nil if there was an error
93 */
94 + (CPData)sendSynchronousRequest:(CPURLRequest)aRequest returningResponse:(/*{*/CPURLResponse/*}*/)aURLResponse
95 {
96  try
97  {
98  var aCFHTTPRequest = new CFHTTPRequest();
99  aCFHTTPRequest.setWithCredentials([aRequest withCredentials]);
100 
101  aCFHTTPRequest.open([aRequest HTTPMethod], [[aRequest URL] absoluteString], NO);
102 
103  var fields = [aRequest allHTTPHeaderFields],
104  key = nil,
105  keys = [fields keyEnumerator];
106 
107  while ((key = [keys nextObject]) !== nil)
108  aCFHTTPRequest.setRequestHeader(key, [fields objectForKey:key]);
109 
110  aCFHTTPRequest.send([aRequest HTTPBody]);
111 
112  if (!aCFHTTPRequest.success())
113  return nil;
114 
115  return [CPData dataWithRawString:aCFHTTPRequest.responseText()];
116  }
117  catch (anException)
118  {
119  }
120 
121  return nil;
122 }
123 
124 /*
125  Creates a url connection with a delegate to monitor the request progress.
126  @param aRequest contains the URL to obtain data from
127  @param aDelegate will be sent messages related to the request progress
128  @return a connection that can be \c started to initiate the request
129 */
130 + (CPURLConnection)connectionWithRequest:(CPURLRequest)aRequest delegate:(id)aDelegate
131 {
132  return [[self alloc] initWithRequest:aRequest delegate:aDelegate];
133 }
134 
135 /*
136  Default class initializer. Use one of the class methods instead.
137  @param aRequest contains the URL to contact
138  @param aDelegate will receive progress messages
139  @param shouldStartImmediately whether the \c -start method should be called from here
140  @return the initialized url connection
141 */
142 - (id)initWithRequest:(CPURLRequest)aRequest delegate:(id)aDelegate startImmediately:(BOOL)shouldStartImmediately
143 {
144  self = [super init];
145 
146  if (self)
147  {
148  _request = aRequest;
149  _originalRequest = [aRequest copy];
150  _delegate = aDelegate;
151  _isCanceled = NO;
152 
153  var URL = [_request URL],
154  scheme = [URL scheme];
155 
156  // Browsers use "file:", Titanium uses "app:"
157  _isLocalFileConnection = scheme === "file" ||
158  ((scheme === "http" || scheme === "https") &&
159  window.location &&
160  (window.location.protocol === "file:" || window.location.protocol === "app:"));
161 
162  _HTTPRequest = new CFHTTPRequest();
163  _HTTPRequest.setWithCredentials([aRequest withCredentials]);
164 
165  if (shouldStartImmediately)
166  [self start];
167  }
168 
169  return self;
170 }
171 
172 - (id)initWithRequest:(CPURLRequest)aRequest delegate:(id)aDelegate
173 {
174  return [self initWithRequest:aRequest delegate:aDelegate startImmediately:YES];
175 }
176 
177 /*
178  return the delegate
179 */
180 - (id)delegate
181 {
182  return _delegate;
183 }
184 
185 /*
186  Start the connection. Not needed if you used the class method +connectionWithRequest:delegate:
187 */
188 - (void)start
189 {
190  _isCanceled = NO;
191 
192  try
193  {
194  _HTTPRequest.open([_request HTTPMethod], [[_request URL] absoluteString], YES);
195 
196  _HTTPRequest.onreadystatechange = function() { [self _readyStateDidChange]; };
197 
198  var fields = [_request allHTTPHeaderFields],
199  key = nil,
200  keys = [fields keyEnumerator];
201 
202  while ((key = [keys nextObject]) !== nil)
203  _HTTPRequest.setRequestHeader(key, [fields objectForKey:key]);
204 
205  _HTTPRequest.send([_request HTTPBody]);
206  }
207  catch (anException)
208  {
209  if ([_delegate respondsToSelector:@selector(connection:didFailWithError:)])
210  [_delegate connection:self didFailWithError:anException];
211  }
212 }
213 
214 /*
215  Cancels the current request.
216 */
217 - (void)cancel
218 {
219  _isCanceled = YES;
220 
221  try
222  {
223  _HTTPRequest.abort();
224  }
225  // We expect an exception in some browsers like FireFox.
226  catch (anException)
227  {
228  }
229 }
230 
231 - (BOOL)isLocalFileConnection
232 {
233  return _isLocalFileConnection;
234 }
235 
236 /* @ignore */
237 - (void)_readyStateDidChange
238 {
239  if (_HTTPRequest.readyState() === CFHTTPRequest.CompleteState)
240  {
241  var statusCode = _HTTPRequest.status(),
242  URL = [_request URL];
243 
244  if (statusCode === 401 && [CPURLConnectionDelegate respondsToSelector:@selector(connectionDidReceiveAuthenticationChallenge:)])
245  [CPURLConnectionDelegate connectionDidReceiveAuthenticationChallenge:self];
246  else
247  {
248  if ([_delegate respondsToSelector:@selector(connection:didReceiveResponse:)])
249  {
250  if (_isLocalFileConnection)
251  [_delegate connection:self didReceiveResponse:[[CPURLResponse alloc] initWithURL:URL]];
252  else
253  {
254  var response = [[CPHTTPURLResponse alloc] initWithURL:URL];
255  [response _setStatusCode:statusCode];
256  [response _setAllResponseHeaders:_HTTPRequest.getAllResponseHeaders()];
257  [_delegate connection:self didReceiveResponse:response];
258  }
259  }
260 
261  if (!_isCanceled)
262  {
263  if ([_delegate respondsToSelector:@selector(connection:didReceiveData:)])
264  [_delegate connection:self didReceiveData:_HTTPRequest.responseText()];
265  if ([_delegate respondsToSelector:@selector(connectionDidFinishLoading:)])
266  [_delegate connectionDidFinishLoading:self];
267  }
268  }
269  }
270 
271  [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
272 }
273 
274 /* @ignore */
275 - (HTTPRequest)_HTTPRequest
276 {
277  return _HTTPRequest;
278 }
279 
280 @end
281 
283 
284 + (CPData)sendSynchronousRequest:(CPURLRequest)aRequest returningResponse:(/*{*/CPURLResponse/*}*/)aURLResponse error:(id)anError
285 {
286  _CPReportLenientDeprecation(self, _cmd, @selector(sendSynchronousRequest:returningResponse:));
287 
288  return [self sendSynchronousRequest:aRequest returningResponse:aURLResponse];
289 }
290 
291 - (HTTPRequest)_XMLHTTPRequest
292 {
293  _CPReportLenientDeprecation(self, _cmd, @selector(_HTTPRequest));
294 
295  return [self _HTTPRequest];
296 }
297 
298 @end
299 
301 
305 - (CPURLRequest)originalRequest
306 {
307  return _originalRequest;
308 }
309 
313 - (CPURLRequest)currentRequest
314 {
315  return _request;
316 }
317 
318 @end