2014-03-10 22:06:52 +01:00
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by Ansible
# still belong to the author of the module, and may assign their own license
# to the complete work.
#
# Copyright (c), Michael DeHaan <michael.dehaan@gmail.com>, 2012-2013
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
try :
import urllib
HAS_URLLIB = True
except :
HAS_URLLIB = False
try :
import urllib2
HAS_URLLIB2 = True
except :
HAS_URLLIB2 = False
try :
import urlparse
HAS_URLPARSE = True
except :
HAS_URLPARSE = False
try :
import ssl
HAS_SSL = True
except :
HAS_SSL = False
2015-05-28 21:35:37 +02:00
HAS_MATCH_HOSTNAME = True
try :
from ssl import match_hostname , CertificateError
except ImportError :
try :
from backports . ssl_match_hostname import match_hostname , CertificateError
except ImportError :
HAS_MATCH_HOSTNAME = False
2014-08-28 15:20:11 +02:00
import httplib
2014-05-16 15:48:29 +02:00
import os
import re
2014-04-15 20:44:43 +02:00
import socket
2014-03-12 19:44:24 +01:00
import tempfile
2015-01-19 14:36:17 +01:00
2014-03-18 23:16:44 +01:00
# This is a dummy cacert provided for Mac OS since you need at least 1
# ca cert, regardless of validity, for Python on Mac OS to use the
# keychain functionality in OpenSSL for validating SSL certificates.
# See: http://mercurial.selenic.com/wiki/CACertificates#Mac_OS_X_10.6_and_higher
DUMMY_CA_CERT = """ -----BEGIN CERTIFICATE-----
MIICvDCCAiWgAwIBAgIJAO8E12S7 / qEpMA0GCSqGSIb3DQEBBQUAMEkxCzAJBgNV
BAYTAlVTMRcwFQYDVQQIEw5Ob3J0aCBDYXJvbGluYTEPMA0GA1UEBxMGRHVyaGFt
MRAwDgYDVQQKEwdBbnNpYmxlMB4XDTE0MDMxODIyMDAyMloXDTI0MDMxNTIyMDAy
MlowSTELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYD
VQQHEwZEdXJoYW0xEDAOBgNVBAoTB0Fuc2libGUwgZ8wDQYJKoZIhvcNAQEBBQAD
gY0AMIGJAoGBANtvpPq3IlNlRbCHhZAcP6WCzhc5RbsDqyh1zrkmLi0GwcQ3z / r9
gaWfQBYhHpobK2Tiq11TfraHeNB3 / VfNImjZcGpN8Fl3MWwu7LfVkJy3gNNnxkA1
4 Go0 / LmIvRFHhbzgfuo9NFgjPmmab9eqXJceqZIlz2C8xA7EeG7ku0 + vAgMBAAGj
gaswgagwHQYDVR0OBBYEFPnN1nPRqNDXGlCqCvdZchRNi / FaMHkGA1UdIwRyMHCA
FPnN1nPRqNDXGlCqCvdZchRNi / FaoU2kSzBJMQswCQYDVQQGEwJVUzEXMBUGA1UE
CBMOTm9ydGggQ2Fyb2xpbmExDzANBgNVBAcTBkR1cmhhbTEQMA4GA1UEChMHQW5z
aWJsZYIJAO8E12S7 / qEpMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEA
MUB80IR6knq9K / tY + hvPsZer6eFMzO3JGkRFBh2kn6JdMDnhYGX7AXVHGflrwNQH
qFy + aenWXsC0ZvrikFxbQnX8GVtDADtVznxOi7XzFw7JOxdsVrpXgSN0eh0aMzvV
zKPZsZ2miVGclicJHzm5q080b1p / sZtuKIEZk6vZqEg =
- - - - - END CERTIFICATE - - - - -
"""
2014-08-28 15:20:11 +02:00
class CustomHTTPSConnection ( httplib . HTTPSConnection ) :
def connect ( self ) :
" Connect to a host on a given (SSL) port. "
2014-09-03 17:04:02 +02:00
if hasattr ( self , ' source_address ' ) :
sock = socket . create_connection ( ( self . host , self . port ) , self . timeout , self . source_address )
else :
sock = socket . create_connection ( ( self . host , self . port ) , self . timeout )
2014-08-28 15:20:11 +02:00
if self . _tunnel_host :
self . sock = sock
self . _tunnel ( )
2015-01-19 14:36:17 +01:00
self . sock = ssl . wrap_socket ( sock , keyfile = self . key_file , certfile = self . cert_file , ssl_version = ssl . PROTOCOL_TLSv1 )
2014-08-28 15:20:11 +02:00
class CustomHTTPSHandler ( urllib2 . HTTPSHandler ) :
def https_open ( self , req ) :
return self . do_open ( CustomHTTPSConnection , req )
https_request = urllib2 . AbstractHTTPHandler . do_request_
2014-05-16 15:48:29 +02:00
def generic_urlparse ( parts ) :
'''
Returns a dictionary of url parts as parsed by urlparse ,
but accounts for the fact that older versions of that
library do not support named attributes ( ie . . netloc )
'''
generic_parts = dict ( )
if hasattr ( parts , ' netloc ' ) :
# urlparse is newer, just read the fields straight
# from the parts object
generic_parts [ ' scheme ' ] = parts . scheme
generic_parts [ ' netloc ' ] = parts . netloc
generic_parts [ ' path ' ] = parts . path
generic_parts [ ' params ' ] = parts . params
generic_parts [ ' query ' ] = parts . query
generic_parts [ ' fragment ' ] = parts . fragment
generic_parts [ ' username ' ] = parts . username
generic_parts [ ' password ' ] = parts . password
generic_parts [ ' hostname ' ] = parts . hostname
generic_parts [ ' port ' ] = parts . port
else :
# we have to use indexes, and then parse out
# the other parts not supported by indexing
generic_parts [ ' scheme ' ] = parts [ 0 ]
generic_parts [ ' netloc ' ] = parts [ 1 ]
generic_parts [ ' path ' ] = parts [ 2 ]
generic_parts [ ' params ' ] = parts [ 3 ]
generic_parts [ ' query ' ] = parts [ 4 ]
generic_parts [ ' fragment ' ] = parts [ 5 ]
# get the username, password, etc.
try :
netloc_re = re . compile ( r ' ^((?: \ w)+(?::(?: \ w)+)?@)?([A-Za-z0-9.-]+)(: \ d+)?$ ' )
( auth , hostname , port ) = netloc_re . match ( parts [ 1 ] )
if port :
# the capture group for the port will include the ':',
# so remove it and convert the port to an integer
port = int ( port [ 1 : ] )
if auth :
# the capture group above inclues the @, so remove it
# and then split it up based on the first ':' found
auth = auth [ : - 1 ]
username , password = auth . split ( ' : ' , 1 )
generic_parts [ ' username ' ] = username
generic_parts [ ' password ' ] = password
generic_parts [ ' hostname ' ] = hostnme
generic_parts [ ' port ' ] = port
except :
generic_parts [ ' username ' ] = None
generic_parts [ ' password ' ] = None
generic_parts [ ' hostname ' ] = None
generic_parts [ ' port ' ] = None
return generic_parts
2014-03-18 23:16:44 +01:00
2014-03-10 22:06:52 +01:00
class RequestWithMethod ( urllib2 . Request ) :
'''
Workaround for using DELETE / PUT / etc with urllib2
Originally contained in library / net_infrastructure / dnsmadeeasy
'''
def __init__ ( self , url , method , data = None , headers = { } ) :
self . _method = method
urllib2 . Request . __init__ ( self , url , data , headers )
def get_method ( self ) :
if self . _method :
return self . _method
else :
return urllib2 . Request . get_method ( self )
class SSLValidationHandler ( urllib2 . BaseHandler ) :
'''
A custom handler class for SSL validation .
Based on :
http : / / stackoverflow . com / questions / 1087227 / validate - ssl - certificates - with - python
http : / / techknack . net / python - urllib2 - handlers /
'''
2014-05-16 15:48:29 +02:00
CONNECT_COMMAND = " CONNECT %s : %s HTTP/1.0 \r \n Connection: close \r \n "
2014-03-10 22:06:52 +01:00
2014-03-12 15:33:19 +01:00
def __init__ ( self , module , hostname , port ) :
2014-03-10 22:06:52 +01:00
self . module = module
self . hostname = hostname
self . port = port
2014-03-12 15:19:35 +01:00
def get_ca_certs ( self ) :
2014-03-10 22:06:52 +01:00
# tries to find a valid CA cert in one of the
# standard locations for the current distribution
2014-03-12 15:19:35 +01:00
ca_certs = [ ]
paths_checked = [ ]
2014-03-10 22:06:52 +01:00
platform = get_platform ( )
distribution = get_distribution ( )
2014-03-12 15:19:35 +01:00
# build a list of paths to check for .crt/.pem files
# based on the platform type
paths_checked . append ( ' /etc/ssl/certs ' )
2014-03-10 22:06:52 +01:00
if platform == ' Linux ' :
2014-03-12 15:19:35 +01:00
paths_checked . append ( ' /etc/pki/ca-trust/extracted/pem ' )
paths_checked . append ( ' /etc/pki/tls/certs ' )
paths_checked . append ( ' /usr/share/ca-certificates/cacert.org ' )
2014-03-10 22:06:52 +01:00
elif platform == ' FreeBSD ' :
2014-03-12 15:19:35 +01:00
paths_checked . append ( ' /usr/local/share/certs ' )
2014-03-10 22:06:52 +01:00
elif platform == ' OpenBSD ' :
2014-03-12 15:19:35 +01:00
paths_checked . append ( ' /etc/ssl ' )
2014-03-10 22:06:52 +01:00
elif platform == ' NetBSD ' :
2014-03-12 15:19:35 +01:00
ca_certs . append ( ' /etc/openssl/certs ' )
2014-08-29 16:32:40 +02:00
elif platform == ' SunOS ' :
paths_checked . append ( ' /opt/local/etc/openssl/certs ' )
2014-03-12 15:19:35 +01:00
# fall back to a user-deployed cert in a standard
# location if the OS platform one is not available
paths_checked . append ( ' /etc/ansible ' )
2014-03-12 19:44:24 +01:00
tmp_fd , tmp_path = tempfile . mkstemp ( )
2014-03-19 15:01:13 +01:00
# Write the dummy ca cert if we are running on Mac OS X
if platform == ' Darwin ' :
os . write ( tmp_fd , DUMMY_CA_CERT )
2014-10-29 14:16:01 +01:00
# Default Homebrew path for OpenSSL certs
paths_checked . append ( ' /usr/local/etc/openssl ' )
2014-03-18 23:16:44 +01:00
2014-03-12 19:44:24 +01:00
# for all of the paths, find any .crt or .pem files
# and compile them into single temp file for use
# in the ssl check to speed up the test
2014-03-12 15:19:35 +01:00
for path in paths_checked :
if os . path . exists ( path ) and os . path . isdir ( path ) :
dir_contents = os . listdir ( path )
for f in dir_contents :
full_path = os . path . join ( path , f )
if os . path . isfile ( full_path ) and os . path . splitext ( f ) [ 1 ] in ( ' .crt ' , ' .pem ' ) :
2014-03-12 19:44:24 +01:00
try :
cert_file = open ( full_path , ' r ' )
os . write ( tmp_fd , cert_file . read ( ) )
2014-04-30 21:46:37 +02:00
os . write ( tmp_fd , ' \n ' )
2014-03-12 19:44:24 +01:00
cert_file . close ( )
except :
pass
2014-03-12 15:19:35 +01:00
2014-03-12 19:44:24 +01:00
return ( tmp_path , paths_checked )
2014-03-10 22:06:52 +01:00
2014-05-16 15:48:29 +02:00
def validate_proxy_response ( self , response , valid_codes = [ 200 ] ) :
'''
make sure we get back a valid code from the proxy
'''
try :
( http_version , resp_code , msg ) = re . match ( r ' (HTTP/ \ d \ . \ d) ( \ d \ d \ d) (.*) ' , response ) . groups ( )
if int ( resp_code ) not in valid_codes :
raise Exception
except :
self . module . fail_json ( msg = ' Connection to proxy failed ' )
2014-12-14 04:12:23 +01:00
def detect_no_proxy ( self , url ) :
'''
Detect if the ' no_proxy ' environment variable is set and honor those locations .
'''
env_no_proxy = os . environ . get ( ' no_proxy ' )
if env_no_proxy :
env_no_proxy = env_no_proxy . split ( ' , ' )
netloc = urlparse . urlparse ( url ) . netloc
for host in env_no_proxy :
if netloc . endswith ( host ) or netloc . split ( ' : ' ) [ 0 ] . endswith ( host ) :
# Our requested URL matches something in no_proxy, so don't
# use the proxy for this
return False
return True
2014-03-10 22:06:52 +01:00
def http_request ( self , req ) :
2014-03-12 19:44:24 +01:00
tmp_ca_cert_path , paths_checked = self . get_ca_certs ( )
2014-05-16 15:48:29 +02:00
https_proxy = os . environ . get ( ' https_proxy ' )
2014-12-14 04:12:23 +01:00
# Detect if 'no_proxy' environment variable is set and if our URL is included
use_proxy = self . detect_no_proxy ( req . get_full_url ( ) )
if not use_proxy :
# ignore proxy settings for this host request
return req
2014-03-12 19:44:24 +01:00
try :
2014-04-15 20:44:43 +02:00
s = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
2014-05-16 15:48:29 +02:00
if https_proxy :
proxy_parts = generic_urlparse ( urlparse . urlparse ( https_proxy ) )
s . connect ( ( proxy_parts . get ( ' hostname ' ) , proxy_parts . get ( ' port ' ) ) )
if proxy_parts . get ( ' scheme ' ) == ' http ' :
s . sendall ( self . CONNECT_COMMAND % ( self . hostname , self . port ) )
if proxy_parts . get ( ' username ' ) :
credentials = " %s : %s " % ( proxy_parts . get ( ' username ' , ' ' ) , proxy_parts . get ( ' password ' , ' ' ) )
s . sendall ( ' Proxy-Authorization: Basic %s \r \n ' % credentials . encode ( ' base64 ' ) . strip ( ) )
s . sendall ( ' \r \n ' )
connect_result = s . recv ( 4096 )
self . validate_proxy_response ( connect_result )
2015-01-19 14:36:17 +01:00
ssl_s = ssl . wrap_socket ( s , ca_certs = tmp_ca_cert_path , cert_reqs = ssl . CERT_REQUIRED )
2015-05-28 21:35:37 +02:00
match_hostname ( ssl_s . getpeercert ( ) , self . hostname )
2014-05-16 15:48:29 +02:00
else :
self . module . fail_json ( msg = ' Unsupported proxy scheme: %s . Currently ansible only supports HTTP proxies. ' % proxy_parts . get ( ' scheme ' ) )
else :
s . connect ( ( self . hostname , self . port ) )
2015-01-19 14:36:17 +01:00
ssl_s = ssl . wrap_socket ( s , ca_certs = tmp_ca_cert_path , cert_reqs = ssl . CERT_REQUIRED )
2015-05-28 21:35:37 +02:00
match_hostname ( ssl_s . getpeercert ( ) , self . hostname )
2014-05-16 15:48:29 +02:00
# close the ssl connection
#ssl_s.unwrap()
s . close ( )
2014-04-15 20:44:43 +02:00
except ( ssl . SSLError , socket . error ) , e :
2014-03-12 15:19:35 +01:00
# fail if we tried all of the certs but none worked
2014-04-15 20:44:43 +02:00
if ' connection refused ' in str ( e ) . lower ( ) :
self . module . fail_json ( msg = ' Failed to connect to %s : %s . ' % ( self . hostname , self . port ) )
else :
self . module . fail_json (
msg = ' Failed to validate the SSL certificate for %s : %s . ' % ( self . hostname , self . port ) + \
' Use validate_certs=no or make sure your managed systems have a valid CA certificate installed. ' + \
' Paths checked for this platform: %s ' % " , " . join ( paths_checked )
)
2015-05-28 21:35:37 +02:00
except CertificateError :
self . module . fail_json ( msg = " SSL Certificate does not belong to %s . Make sure the url has a certificate that belongs to it or use validate_certs=no (insecure) " % self . hostname )
2014-03-12 19:44:24 +01:00
try :
# cleanup the temp file created, don't worry
# if it fails for some reason
os . remove ( tmp_ca_cert_path )
except :
pass
2014-03-10 22:06:52 +01:00
return req
https_request = http_request
def url_argument_spec ( ) :
'''
Creates an argument spec that can be used with any module
that will be requesting content via urllib / urllib2
'''
return dict (
url = dict ( ) ,
force = dict ( default = ' no ' , aliases = [ ' thirsty ' ] , type = ' bool ' ) ,
http_agent = dict ( default = ' ansible-httpget ' ) ,
use_proxy = dict ( default = ' yes ' , type = ' bool ' ) ,
validate_certs = dict ( default = ' yes ' , type = ' bool ' ) ,
2014-04-23 20:44:36 +02:00
url_username = dict ( required = False ) ,
url_password = dict ( required = False ) ,
2014-03-10 22:06:52 +01:00
)
def fetch_url ( module , url , data = None , headers = None , method = None ,
2014-05-20 06:04:13 +02:00
use_proxy = True , force = False , last_mod_time = None , timeout = 10 ) :
2014-03-10 22:06:52 +01:00
'''
Fetches a file from an HTTP / FTP server using urllib2
'''
if not HAS_URLLIB :
module . fail_json ( msg = ' urllib is not installed ' )
if not HAS_URLLIB2 :
module . fail_json ( msg = ' urllib2 is not installed ' )
elif not HAS_URLPARSE :
module . fail_json ( msg = ' urlparse is not installed ' )
r = None
handlers = [ ]
info = dict ( url = url )
2014-04-18 00:07:07 +02:00
distribution = get_distribution ( )
2014-03-12 16:19:22 +01:00
# Get validate_certs from the module params
validate_certs = module . params . get ( ' validate_certs ' , True )
2014-05-16 15:48:29 +02:00
# FIXME: change the following to use the generic_urlparse function
# to remove the indexed references for 'parsed'
2014-03-10 22:06:52 +01:00
parsed = urlparse . urlparse ( url )
2015-05-28 21:35:37 +02:00
if parsed [ 0 ] == ' https ' and validate_certs :
if not HAS_SSL :
2014-04-18 00:07:07 +02:00
if distribution == ' Redhat ' :
module . fail_json ( msg = ' SSL validation is not available in your version of python. You can use validate_certs=no, however this is unsafe and not recommended. You can also install python-ssl from EPEL ' )
else :
module . fail_json ( msg = ' SSL validation is not available in your version of python. You can use validate_certs=no, however this is unsafe and not recommended ' )
2015-05-28 21:35:37 +02:00
if not HAS_MATCH_HOSTNAME :
module . fail_json ( msg = ' Available SSL validation does not check that the certificate matches the hostname. You can install backports.ssl_match_hostname or update your managed machine to python-2.7.9 or newer. You could also use validate_certs=no, however this is unsafe and not recommended ' )
# do the cert validation
netloc = parsed [ 1 ]
if ' @ ' in netloc :
netloc = netloc . split ( ' @ ' , 1 ) [ 1 ]
if ' : ' in netloc :
hostname , port = netloc . split ( ' : ' , 1 )
port = int ( port )
else :
hostname = netloc
port = 443
# create the SSL validation handler and
# add it to the list of handlers
ssl_handler = SSLValidationHandler ( module , hostname , port )
handlers . append ( ssl_handler )
2014-03-10 22:06:52 +01:00
2014-04-23 20:44:36 +02:00
if parsed [ 0 ] != ' ftp ' :
2014-04-24 07:44:39 +02:00
username = module . params . get ( ' url_username ' , ' ' )
if username :
2014-04-23 20:44:36 +02:00
password = module . params . get ( ' url_password ' , ' ' )
netloc = parsed [ 1 ]
elif ' @ ' in parsed [ 1 ] :
credentials , netloc = parsed [ 1 ] . split ( ' @ ' , 1 )
if ' : ' in credentials :
username , password = credentials . split ( ' : ' , 1 )
else :
username = credentials
password = ' '
parsed = list ( parsed )
parsed [ 1 ] = netloc
2014-03-10 22:06:52 +01:00
2014-04-24 07:44:39 +02:00
# reconstruct url without credentials
url = urlparse . urlunparse ( parsed )
2014-03-10 22:06:52 +01:00
2014-04-24 07:44:39 +02:00
if username :
passman = urllib2 . HTTPPasswordMgrWithDefaultRealm ( )
2014-03-10 22:06:52 +01:00
2014-04-24 07:44:39 +02:00
# this creates a password manager
passman . add_password ( None , netloc , username , password )
# because we have put None at the start it will always
# use this username/password combination for urls
# for which `theurl` is a super-url
authhandler = urllib2 . HTTPBasicAuthHandler ( passman )
# create the AuthHandler
handlers . append ( authhandler )
2014-03-10 22:06:52 +01:00
if not use_proxy :
proxyhandler = urllib2 . ProxyHandler ( { } )
handlers . append ( proxyhandler )
2014-09-05 20:48:45 +02:00
# pre-2.6 versions of python cannot use the custom https
# handler, since the socket class is lacking this method
if hasattr ( socket , ' create_connection ' ) :
handlers . append ( CustomHTTPSHandler )
2014-08-28 15:20:11 +02:00
2014-03-10 22:06:52 +01:00
opener = urllib2 . build_opener ( * handlers )
urllib2 . install_opener ( opener )
if method :
if method . upper ( ) not in ( ' OPTIONS ' , ' GET ' , ' HEAD ' , ' POST ' , ' PUT ' , ' DELETE ' , ' TRACE ' , ' CONNECT ' ) :
module . fail_json ( msg = ' invalid HTTP request method; %s ' % method . upper ( ) )
request = RequestWithMethod ( url , method . upper ( ) , data )
else :
request = urllib2 . Request ( url , data )
# add the custom agent header, to help prevent issues
# with sites that block the default urllib agent string
request . add_header ( ' User-agent ' , module . params . get ( ' http_agent ' ) )
# if we're ok with getting a 304, set the timestamp in the
# header, otherwise make sure we don't get a cached copy
if last_mod_time and not force :
tstamp = last_mod_time . strftime ( ' %a , %d % b % Y % H: % M: % S +0000 ' )
request . add_header ( ' If-Modified-Since ' , tstamp )
else :
request . add_header ( ' cache-control ' , ' no-cache ' )
# user defined headers now, which may override things we've set above
if headers :
if not isinstance ( headers , dict ) :
module . fail_json ( " headers provided to fetch_url() must be a dict " )
for header in headers :
request . add_header ( header , headers [ header ] )
try :
if sys . version_info < ( 2 , 6 , 0 ) :
# urlopen in python prior to 2.6.0 did not
# have a timeout parameter
r = urllib2 . urlopen ( request , None )
else :
r = urllib2 . urlopen ( request , None , timeout )
info . update ( r . info ( ) )
info [ ' url ' ] = r . geturl ( ) # The URL goes in too, because of redirects.
info . update ( dict ( msg = " OK ( %s bytes) " % r . headers . get ( ' Content-Length ' , ' unknown ' ) , status = 200 ) )
except urllib2 . HTTPError , e :
info . update ( dict ( msg = str ( e ) , status = e . code ) )
except urllib2 . URLError , e :
code = int ( getattr ( e , ' code ' , - 1 ) )
info . update ( dict ( msg = " Request failed: %s " % str ( e ) , status = code ) )
2014-09-11 16:46:53 +02:00
except socket . error , e :
info . update ( dict ( msg = " Connection failure: %s " % str ( e ) , status = - 1 ) )
except Exception , e :
info . update ( dict ( msg = " An unknown error occurred: %s " % str ( e ) , status = - 1 ) )
2014-03-10 22:06:52 +01:00
return r , info