243 connection.send(self, chunk) |
243 connection.send(self, chunk) |
244 else: |
244 else: |
245 connection.send(self, data) |
245 connection.send(self, data) |
246 return _sendfile |
246 return _sendfile |
247 |
247 |
|
248 has_https = hasattr(urllib2, 'HTTPSHandler') |
|
249 if has_https: |
|
250 try: |
|
251 # avoid using deprecated/broken FakeSocket in python 2.6 |
|
252 import ssl |
|
253 _ssl_wrap_socket = ssl.wrap_socket |
|
254 except ImportError: |
|
255 def _ssl_wrap_socket(sock, key_file, cert_file): |
|
256 ssl = socket.ssl(sock, key_file, cert_file) |
|
257 return httplib.FakeSocket(sock, ssl) |
|
258 |
248 class httpconnection(keepalive.HTTPConnection): |
259 class httpconnection(keepalive.HTTPConnection): |
249 # must be able to send big bundle as stream. |
260 # must be able to send big bundle as stream. |
250 send = _gen_sendfile(keepalive.HTTPConnection) |
261 send = _gen_sendfile(keepalive.HTTPConnection) |
251 |
262 |
|
263 def _proxytunnel(self): |
|
264 proxyheaders = dict( |
|
265 [(x, self.headers[x]) for x in self.headers |
|
266 if x.lower().startswith('proxy-')]) |
|
267 self._set_hostport(self.host, self.port) |
|
268 self.send('CONNECT %s:%d HTTP/1.0\r\n' % (self.realhost, self.realport)) |
|
269 for header in proxyheaders.iteritems(): |
|
270 self.send('%s: %s\r\n' % header) |
|
271 self.send('\r\n') |
|
272 |
|
273 # majority of the following code is duplicated from |
|
274 # httplib.HTTPConnection as there are no adequate places to |
|
275 # override functions to provide the needed functionality |
|
276 res = self.response_class(self.sock, |
|
277 strict=self.strict, |
|
278 method=self._method) |
|
279 |
|
280 while True: |
|
281 version, status, reason = res._read_status() |
|
282 if status != httplib.CONTINUE: |
|
283 break |
|
284 while True: |
|
285 skip = res.fp.readline().strip() |
|
286 if not skip: |
|
287 break |
|
288 res.status = status |
|
289 res.reason = reason.strip() |
|
290 |
|
291 if res.status == 200: |
|
292 while True: |
|
293 line = res.fp.readline() |
|
294 if line == '\r\n': |
|
295 break |
|
296 return True |
|
297 |
|
298 if version == 'HTTP/1.0': |
|
299 res.version = 10 |
|
300 elif version.startswith('HTTP/1.'): |
|
301 res.version = 11 |
|
302 elif version == 'HTTP/0.9': |
|
303 res.version = 9 |
|
304 else: |
|
305 raise httplib.UnknownProtocol(version) |
|
306 |
|
307 if res.version == 9: |
|
308 res.length = None |
|
309 res.chunked = 0 |
|
310 res.will_close = 1 |
|
311 res.msg = httplib.HTTPMessage(cStringIO.StringIO()) |
|
312 return False |
|
313 |
|
314 res.msg = httplib.HTTPMessage(res.fp) |
|
315 res.msg.fp = None |
|
316 |
|
317 # are we using the chunked-style of transfer encoding? |
|
318 trenc = res.msg.getheader('transfer-encoding') |
|
319 if trenc and trenc.lower() == "chunked": |
|
320 res.chunked = 1 |
|
321 res.chunk_left = None |
|
322 else: |
|
323 res.chunked = 0 |
|
324 |
|
325 # will the connection close at the end of the response? |
|
326 res.will_close = res._check_close() |
|
327 |
|
328 # do we have a Content-Length? |
|
329 # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked" |
|
330 length = res.msg.getheader('content-length') |
|
331 if length and not res.chunked: |
|
332 try: |
|
333 res.length = int(length) |
|
334 except ValueError: |
|
335 res.length = None |
|
336 else: |
|
337 if res.length < 0: # ignore nonsensical negative lengths |
|
338 res.length = None |
|
339 else: |
|
340 res.length = None |
|
341 |
|
342 # does the body have a fixed length? (of zero) |
|
343 if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or |
|
344 100 <= status < 200 or # 1xx codes |
|
345 res._method == 'HEAD'): |
|
346 res.length = 0 |
|
347 |
|
348 # if the connection remains open, and we aren't using chunked, and |
|
349 # a content-length was not provided, then assume that the connection |
|
350 # WILL close. |
|
351 if (not res.will_close and |
|
352 not res.chunked and |
|
353 res.length is None): |
|
354 res.will_close = 1 |
|
355 |
|
356 self.proxyres = res |
|
357 |
|
358 return False |
|
359 |
|
360 def connect(self): |
|
361 if has_https and self.realhost: # use CONNECT proxy |
|
362 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
363 self.sock.connect((self.host, self.port)) |
|
364 if self._proxytunnel(): |
|
365 # we do not support client x509 certificates |
|
366 self.sock = _ssl_wrap_socket(self.sock, None, None) |
|
367 else: |
|
368 keepalive.HTTPConnection.connect(self) |
|
369 |
|
370 def getresponse(self): |
|
371 proxyres = getattr(self, 'proxyres', None) |
|
372 if proxyres: |
|
373 if proxyres.will_close: |
|
374 self.close() |
|
375 self.proxyres = None |
|
376 return proxyres |
|
377 return keepalive.HTTPConnection.getresponse(self) |
|
378 |
252 class httphandler(keepalive.HTTPHandler): |
379 class httphandler(keepalive.HTTPHandler): |
253 def http_open(self, req): |
380 def http_open(self, req): |
254 return self.do_open(httpconnection, req) |
381 return self.do_open(httpconnection, req) |
255 |
382 |
|
383 def _start_transaction(self, h, req): |
|
384 if req.get_selector() == req.get_full_url(): # has proxy |
|
385 urlparts = urlparse.urlparse(req.get_selector()) |
|
386 if urlparts[0] == 'https': # only use CONNECT for HTTPS |
|
387 if ':' in urlparts[1]: |
|
388 realhost, realport = urlparts[1].split(':') |
|
389 else: |
|
390 realhost = urlparts[1] |
|
391 realport = 443 |
|
392 |
|
393 h.realhost = realhost |
|
394 h.realport = realport |
|
395 h.headers = req.headers.copy() |
|
396 h.headers.update(self.parent.addheaders) |
|
397 return keepalive.HTTPHandler._start_transaction(self, h, req) |
|
398 |
|
399 h.realhost = None |
|
400 h.realport = None |
|
401 h.headers = None |
|
402 return keepalive.HTTPHandler._start_transaction(self, h, req) |
|
403 |
256 def __del__(self): |
404 def __del__(self): |
257 self.close_all() |
405 self.close_all() |
258 |
406 |
259 has_https = hasattr(urllib2, 'HTTPSHandler') |
|
260 if has_https: |
407 if has_https: |
261 class httpsconnection(httplib.HTTPSConnection): |
408 class httpsconnection(httplib.HTTPSConnection): |
262 response_class = keepalive.HTTPResponse |
409 response_class = keepalive.HTTPResponse |
263 # must be able to send big bundle as stream. |
410 # must be able to send big bundle as stream. |
264 send = _gen_sendfile(httplib.HTTPSConnection) |
411 send = _gen_sendfile(httplib.HTTPSConnection) |