API  0.9.6
 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  return [CPData dataWithRawString:request.responseText()];
110  }
111  catch (anException)
112  {
113  }
114 
115  return nil;
116 }
117 
118 /*
119  Creates a url connection with a delegate to monitor the request progress.
120  @param aRequest contains the URL to obtain data from
121  @param aDelegate will be sent messages related to the request progress
122  @return a connection that can be \c started to initiate the request
123 */
124 + (CPURLConnection)connectionWithRequest:(CPURLRequest)aRequest delegate:(id)aDelegate
125 {
126  return [[self alloc] initWithRequest:aRequest delegate:aDelegate];
127 }
128 
129 /*
130  Default class initializer. Use one of the class methods instead.
131  @param aRequest contains the URL to contact
132  @param aDelegate will receive progress messages
133  @param shouldStartImmediately whether the \c -start method should be called from here
134  @return the initialized url connection
135 */
136 - (id)initWithRequest:(CPURLRequest)aRequest delegate:(id)aDelegate startImmediately:(BOOL)shouldStartImmediately
137 {
138  self = [super init];
139 
140  if (self)
141  {
142  _request = aRequest;
143  _delegate = aDelegate;
144  _isCanceled = NO;
145 
146  var URL = [_request URL],
147  scheme = [URL scheme];
148 
149  // Browsers use "file:", Titanium uses "app:"
150  _isLocalFileConnection = scheme === "file" ||
151  ((scheme === "http" || scheme === "https:") &&
152  window.location &&
153  (window.location.protocol === "file:" || window.location.protocol === "app:"));
154 
155  _HTTPRequest = new CFHTTPRequest();
156 
157  if (shouldStartImmediately)
158  [self start];
159  }
160 
161  return self;
162 }
163 
164 - (id)initWithRequest:(CPURLRequest)aRequest delegate:(id)aDelegate
165 {
166  return [self initWithRequest:aRequest delegate:aDelegate startImmediately:YES];
167 }
168 
169 /*
170  return the delegate
171 */
172 - (id)delegate
173 {
174  return _delegate;
175 }
176 
177 /*
178  Start the connection. Not needed if you used the class method +connectionWithRequest:delegate:
179 */
180 - (void)start
181 {
182  _isCanceled = NO;
183 
184  try
185  {
186  _HTTPRequest.open([_request HTTPMethod], [[_request URL] absoluteString], YES);
187 
188  _HTTPRequest.onreadystatechange = function() { [self _readyStateDidChange]; };
189 
190  var fields = [_request allHTTPHeaderFields],
191  key = nil,
192  keys = [fields keyEnumerator];
193 
194  while ((key = [keys nextObject]) !== nil)
195  _HTTPRequest.setRequestHeader(key, [fields objectForKey:key]);
196 
197  _HTTPRequest.send([_request HTTPBody]);
198  }
199  catch (anException)
200  {
201  if ([_delegate respondsToSelector:@selector(connection:didFailWithError:)])
202  [_delegate connection:self didFailWithError:anException];
203  }
204 }
205 
206 /*
207  Cancels the current request.
208 */
209 - (void)cancel
210 {
211  _isCanceled = YES;
212 
213  try
214  {
215  _HTTPRequest.abort();
216  }
217  // We expect an exception in some browsers like FireFox.
218  catch (anException)
219  {
220  }
221 }
222 
223 - (BOOL)isLocalFileConnection
224 {
225  return _isLocalFileConnection;
226 }
227 
228 /* @ignore */
229 - (void)_readyStateDidChange
230 {
231  if (_HTTPRequest.readyState() === CFHTTPRequest.CompleteState)
232  {
233  var statusCode = _HTTPRequest.status(),
234  URL = [_request URL];
235 
236  if (statusCode === 401 && [CPURLConnectionDelegate respondsToSelector:@selector(connectionDidReceiveAuthenticationChallenge:)])
237  [CPURLConnectionDelegate connectionDidReceiveAuthenticationChallenge:self];
238  else
239  {
240  if ([_delegate respondsToSelector:@selector(connection:didReceiveResponse:)])
241  {
242  if (_isLocalFileConnection)
243  [_delegate connection:self didReceiveResponse:[[CPURLResponse alloc] initWithURL:URL]];
244  else
245  {
246  var response = [[CPHTTPURLResponse alloc] initWithURL:URL];
247  [response _setStatusCode:statusCode];
248  [response _setAllResponseHeaders:_HTTPRequest.getAllResponseHeaders()];
249  [_delegate connection:self didReceiveResponse:response];
250  }
251  }
252  if (!_isCanceled)
253  {
254  if ([_delegate respondsToSelector:@selector(connection:didReceiveData:)])
255  [_delegate connection:self didReceiveData:_HTTPRequest.responseText()];
256  if ([_delegate respondsToSelector:@selector(connectionDidFinishLoading:)])
257  [_delegate connectionDidFinishLoading:self];
258  }
259  }
260  }
261 
262  [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
263 }
264 
265 /* @ignore */
266 - (HTTPRequest)_HTTPRequest
267 {
268  return _HTTPRequest;
269 }
270 
271 @end
272 
274 
275 + (CPData)sendSynchronousRequest:(CPURLRequest)aRequest returningResponse:({CPURLResponse})aURLResponse error:(id)anError
276 {
277  _CPReportLenientDeprecation(self, _cmd, @selector(sendSynchronousRequest:returningResponse:));
278 
279  return [self sendSynchronousRequest:aRequest returningResponse:aURLResponse];
280 }
281 
282 - (HTTPRequest)_XMLHTTPRequest
283 {
284  _CPReportLenientDeprecation(self, _cmd, @selector(_HTTPRequest));
285 
286  return [self _HTTPRequest];
287 }
288 
289 @end