@ -816,147 +816,328 @@ def maybe_add_ssl_handler(url, validate_certs):
return SSLValidationHandler ( hostname , port )
def open_url ( url , data = None , headers = None , method = None , use_proxy = True ,
force = False , last_mod_time = None , timeout = 10 , validate_certs = True ,
class Request :
def __init__ ( self , headers = None , use_proxy = True , force = False , timeout = 10 , validate_certs = True ,
url_username = None , url_password = None , http_agent = None , force_basic_auth = False ,
follow_redirects = ' urllib2 ' , client_cert = None , client_key = None , cookies = None ) :
""" This class works somewhat similarly to the ``Session`` class of from requests
by defining a cookiejar that an be used across requests as well as cascaded defaults that
can apply to repeated requests
For documentation of params , see ` ` Request . open ` `
>> > from ansible . module_utils . urls import Request
>> > r = Request ( )
>> > r . open ( ' GET ' , ' http://httpbin.org/cookies/set?k1=v1 ' ) . read ( )
' { \n " cookies " : { \n " k1 " : " v1 " \n } \n } \n '
>> > r = Request ( url_username = ' user ' , url_password = ' passwd ' )
>> > r . open ( ' GET ' , ' http://httpbin.org/basic-auth/user/passwd ' ) . read ( )
' { \n " authenticated " : true, \n " user " : " user " \n } \n '
>> > r = Request ( headers = dict ( foo = ' bar ' ) )
>> > r . open ( ' GET ' , ' http://httpbin.org/get ' , headers = dict ( baz = ' qux ' ) ) . read ( )
"""
self . headers = headers or { }
if not isinstance ( self . headers , dict ) :
raise ValueError ( " headers must be a dict " )
self . use_proxy = use_proxy
self . force = force
self . timeout = timeout
self . validate_certs = validate_certs
self . url_username = url_username
self . url_password = url_password
self . http_agent = http_agent
self . force_basic_auth = force_basic_auth
self . follow_redirects = follow_redirects
self . client_cert = client_cert
self . client_key = client_key
if isinstance ( cookies , cookiejar . CookieJar ) :
self . cookies = cookies
else :
self . cookies = cookiejar . CookieJar ( )
def _fallback ( self , value , fallback ) :
if value is None :
return fallback
return value
def open ( self , method , url , data = None , headers = None , use_proxy = None ,
force = None , last_mod_time = None , timeout = None , validate_certs = None ,
url_username = None , url_password = None , http_agent = None ,
force_basic_auth = False , follow_redirects = ' urllib2 ' ,
force_basic_auth = None, follow_redirects = None ,
client_cert = None , client_key = None , cookies = None ) :
'''
Sends a request via HTTP ( S ) or FTP using urllib2 ( Python2 ) or urllib ( Python3 )
"""
Sends a request via HTTP ( S ) or FTP using urllib2 ( Python2 ) or urllib ( Python3 )
Does not require the module environment
Returns : class : ` HTTPResponse ` object .
: arg method : method for the request
: arg url : URL to request
: kwarg data : ( optional ) bytes , or file - like object to send
in the body of the request
: kwarg headers : ( optional ) Dictionary of HTTP Headers to send with the
request
: kwarg use_proxy : ( optional ) Boolean of whether or not to use proxy
: kwarg force : ( optional ) Boolean of whether or not to set ` cache - control : no - cache ` header
: kwarg last_mod_time : ( optional ) Datetime object to use when setting If - Modified - Since header
: kwarg timeout : ( optional ) How long to wait for the server to send
data before giving up , as a float
: kwarg validate_certs : ( optional ) Booleani that controls whether we verify
the server ' s TLS certificate
: kwarg url_username : ( optional ) String of the user to use when authenticating
: kwarg url_password : ( optional ) String of the password to use when authenticating
: kwarg http_agent : ( optional ) String of the User - Agent to use in the request
: kwarg force_basic_auth : ( optional ) Boolean determining if auth header should be sent in the initial request
: kwarg follow_redirects : ( optional ) String of urllib2 , all / yes , safe , none to determine how redirects are
followed , see RedirectHandlerFactory for more information
: kwarg client_cert : ( optional ) PEM formatted certificate chain file to be used for SSL client authentication .
This file can also include the key as well , and if the key is included , client_key is not required
: kwarg client_key : ( optional ) PEM formatted file that contains your private key to be used for SSL client
authentication . If client_cert contains both the certificate and key , this option is not required
: kwarg cookies : ( optional ) CookieJar object to send with the
request
: returns : HTTPResponse
"""
Does not require the module environment
'''
handlers = [ ]
ssl_handler = maybe_add_ssl_handler ( url , validate_certs )
if ssl_handler :
handlers . append ( ssl_handler )
parsed = generic_urlparse ( urlparse ( url ) )
if parsed . scheme != ' ftp ' :
username = url_username
method = method . upper ( )
if headers is None :
headers = { }
elif not isinstance ( headers , dict ) :
raise ValueError ( " headers must be a dict " )
headers = dict ( self . headers , * * headers )
use_proxy = self . _fallback ( use_proxy , self . use_proxy )
force = self . _fallback ( force , self . force )
timeout = self . _fallback ( timeout , self . timeout )
validate_certs = self . _fallback ( validate_certs , self . validate_certs )
url_username = self . _fallback ( url_username , self . url_username )
url_password = self . _fallback ( url_password , self . url_password )
http_agent = self . _fallback ( http_agent , self . http_agent )
force_basic_auth = self . _fallback ( force_basic_auth , self . force_basic_auth )
follow_redirects = self . _fallback ( follow_redirects , self . follow_redirects )
client_cert = self . _fallback ( client_cert , self . client_cert )
client_key = self . _fallback ( client_key , self . client_key )
cookies = self . _fallback ( cookies , self . cookies )
handlers = [ ]
ssl_handler = maybe_add_ssl_handler ( url , validate_certs )
if ssl_handler :
handlers . append ( ssl_handler )
parsed = generic_urlparse ( urlparse ( url ) )
if parsed . scheme != ' ftp ' :
username = url_username
if username :
password = url_password
netloc = parsed . netloc
elif ' @ ' in parsed . netloc :
credentials , netloc = parsed . netloc . split ( ' @ ' , 1 )
if ' : ' in credentials :
username , password = credentials . split ( ' : ' , 1 )
else :
username = credentials
password = ' '
if username :
password = url_password
netloc = parsed . netloc
elif ' @ ' in parsed . netloc :
credentials , netloc = parsed . netloc . split ( ' @ ' , 1 )
if ' : ' in credentials :
username , password = credentials . split ( ' : ' , 1 )
else :
username = credentials
password = ' '
parsed_list = parsed . as_list ( )
parsed_list [ 1 ] = netloc
parsed_list = parsed . as_list ( )
parsed_list [ 1 ] = netloc
# reconstruct url without credentials
url = urlunparse ( parsed_list )
# reconstruct url without credentials
url = urlunparse ( parsed_list )
if username and not force_basic_auth :
passman = urllib_request . HTTPPasswordMgrWithDefaultRealm ( )
if username and not force_basic_auth :
passman = urllib_request . HTTPPasswordMgrWithDefaultRealm ( )
# this creates a password manager
passman . add_password ( None , netloc , username , password )
# 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 = urllib_request . HTTPBasicAuthHandler ( passman )
digest_authhandler = urllib_request . HTTPDigestAuthHandler ( passman )
# 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 = urllib_request . HTTPBasicAuthHandler ( passman )
digest_authhandler = urllib_request . HTTPDigestAuthHandler ( passman )
# create the AuthHandler
handlers . append ( authhandler )
handlers . append ( digest_authhandler )
# create the AuthHandler
handlers . append ( authhandler )
handlers . append ( digest_authhandler )
elif username and force_basic_auth :
headers [ " Authorization " ] = basic_auth_header ( username , password )
elif username and force_basic_auth :
headers [ " Authorization " ] = basic_auth_header ( username , password )
else :
try :
rc = netrc . netrc ( os . environ . get ( ' NETRC ' ) )
login = rc . authenticators ( parsed . hostname )
except IOError :
login = None
else :
try :
rc = netrc . netrc ( os . environ . get ( ' NETRC ' ) )
login = rc . authenticators ( parsed . hostname )
except IOError :
login = None
if login :
username , _ , password = login
if username and password :
headers [ " Authorization " ] = basic_auth_header ( username , password )
if not use_proxy :
proxyhandler = urllib_request . ProxyHandler ( { } )
handlers . append ( proxyhandler )
if HAS_SSLCONTEXT and not validate_certs :
# In 2.7.9, the default context validates certificates
context = SSLContext ( ssl . PROTOCOL_SSLv23 )
if ssl . OP_NO_SSLv2 :
context . options | = ssl . OP_NO_SSLv2
context . options | = ssl . OP_NO_SSLv3
context . verify_mode = ssl . CERT_NONE
context . check_hostname = False
handlers . append ( HTTPSClientAuthHandler ( client_cert = client_cert ,
client_key = client_key ,
context = context ) )
elif client_cert :
handlers . append ( HTTPSClientAuthHandler ( client_cert = client_cert ,
client_key = client_key ) )
# pre-2.6 versions of python cannot use the custom https
# handler, since the socket class is lacking create_connection.
# Some python builds lack HTTPS support.
if hasattr ( socket , ' create_connection ' ) and CustomHTTPSHandler :
handlers . append ( CustomHTTPSHandler )
handlers . append ( RedirectHandlerFactory ( follow_redirects , validate_certs ) )
# add some nicer cookie handling
if cookies is not None :
handlers . append ( urllib_request . HTTPCookieProcessor ( cookies ) )
opener = urllib_request . build_opener ( * handlers )
urllib_request . install_opener ( opener )
data = to_bytes ( data , nonstring = ' passthru ' )
if method :
if method . upper ( ) not in ( ' OPTIONS ' , ' GET ' , ' HEAD ' , ' POST ' , ' PUT ' , ' DELETE ' , ' TRACE ' , ' CONNECT ' , ' PATCH ' ) :
raise ConnectionError ( ' invalid HTTP request method; %s ' % method . upper ( ) )
request = RequestWithMethod ( url , method . upper ( ) , data )
else :
request = urllib_request . Request ( url , data )
# add the custom agent header, to help prevent issues
# with sites that block the default urllib agent string
if http_agent :
request . add_header ( ' User-agent ' , http_agent )
# Cache control
# Either we directly force a cache refresh
if force :
request . add_header ( ' cache-control ' , ' no-cache ' )
# or we do it if the original is more recent than our copy
elif last_mod_time :
tstamp = last_mod_time . strftime ( ' %a , %d % b % Y % H: % M: % S +0000 ' )
request . add_header ( ' If-Modified-Since ' , tstamp )
# user defined headers now, which may override things we've set above
if headers :
if not isinstance ( headers , dict ) :
raise ValueError ( " headers provided to open_url() must be a dict " )
if login :
username , _ , password = login
if username and password :
headers [ " Authorization " ] = basic_auth_header ( username , password )
if not use_proxy :
proxyhandler = urllib_request . ProxyHandler ( { } )
handlers . append ( proxyhandler )
if HAS_SSLCONTEXT and not validate_certs :
# In 2.7.9, the default context validates certificates
context = SSLContext ( ssl . PROTOCOL_SSLv23 )
if ssl . OP_NO_SSLv2 :
context . options | = ssl . OP_NO_SSLv2
context . options | = ssl . OP_NO_SSLv3
context . verify_mode = ssl . CERT_NONE
context . check_hostname = False
handlers . append ( HTTPSClientAuthHandler ( client_cert = client_cert ,
client_key = client_key ,
context = context ) )
elif client_cert :
handlers . append ( HTTPSClientAuthHandler ( client_cert = client_cert ,
client_key = client_key ) )
# pre-2.6 versions of python cannot use the custom https
# handler, since the socket class is lacking create_connection.
# Some python builds lack HTTPS support.
if hasattr ( socket , ' create_connection ' ) and CustomHTTPSHandler :
handlers . append ( CustomHTTPSHandler )
handlers . append ( RedirectHandlerFactory ( follow_redirects , validate_certs ) )
# add some nicer cookie handling
if cookies is not None :
handlers . append ( urllib_request . HTTPCookieProcessor ( cookies ) )
opener = urllib_request . build_opener ( * handlers )
urllib_request . install_opener ( opener )
data = to_bytes ( data , nonstring = ' passthru ' )
if method not in ( ' OPTIONS ' , ' GET ' , ' HEAD ' , ' POST ' , ' PUT ' , ' DELETE ' , ' TRACE ' , ' CONNECT ' , ' PATCH ' ) :
raise ConnectionError ( ' invalid HTTP request method; %s ' % method )
request = RequestWithMethod ( url , method , data )
# add the custom agent header, to help prevent issues
# with sites that block the default urllib agent string
if http_agent :
request . add_header ( ' User-agent ' , http_agent )
# Cache control
# Either we directly force a cache refresh
if force :
request . add_header ( ' cache-control ' , ' no-cache ' )
# or we do it if the original is more recent than our copy
elif last_mod_time :
tstamp = last_mod_time . strftime ( ' %a , %d % b % Y % H: % M: % S +0000 ' )
request . add_header ( ' If-Modified-Since ' , tstamp )
# user defined headers now, which may override things we've set above
for header in headers :
request . add_header ( header , headers [ header ] )
urlopen_args = [ request , None ]
if sys . version_info > = ( 2 , 6 , 0 ) :
# urlopen in python prior to 2.6.0 did not
# have a timeout parameter
urlopen_args . append ( timeout )
urlopen_args = [ request , None ]
if sys . version_info > = ( 2 , 6 , 0 ) :
# urlopen in python prior to 2.6.0 did not
# have a timeout parameter
urlopen_args . append ( timeout )
r = urllib_request . urlopen ( * urlopen_args )
return r
def get ( self , url , * * kwargs ) :
r """ Sends a GET request. Returns :class:`HTTPResponse` object.
: arg url : URL to request
: kwarg \* \* kwargs : Optional arguments that ` ` open ` ` takes .
: returns : HTTPResponse
"""
return self . open ( ' GET ' , url , * * kwargs )
def options ( self , url , * * kwargs ) :
r """ Sends a OPTIONS request. Returns :class:`HTTPResponse` object.
: arg url : URL to request
: kwarg \* \* kwargs : Optional arguments that ` ` open ` ` takes .
: returns : HTTPResponse
"""
return self . open ( ' OPTIONS ' , url , * * kwargs )
def head ( self , url , * * kwargs ) :
r """ Sends a HEAD request. Returns :class:`HTTPResponse` object.
: arg url : URL to request
: kwarg \* \* kwargs : Optional arguments that ` ` open ` ` takes .
: returns : HTTPResponse
"""
return self . open ( ' HEAD ' , url , * * kwargs )
def post ( self , url , data = None , * * kwargs ) :
r """ Sends a POST request. Returns :class:`HTTPResponse` object.
: arg url : URL to request .
: kwarg data : ( optional ) bytes , or file - like object to send in the body of the request .
: kwarg \* \* kwargs : Optional arguments that ` ` open ` ` takes .
: returns : HTTPResponse
"""
return self . open ( ' POST ' , url , data = data , * * kwargs )
def put ( self , url , data = None , * * kwargs ) :
r """ Sends a PUT request. Returns :class:`HTTPResponse` object.
: arg url : URL to request .
: kwarg data : ( optional ) bytes , or file - like object to send in the body of the request .
: kwarg \* \* kwargs : Optional arguments that ` ` open ` ` takes .
: returns : HTTPResponse
"""
return self . open ( ' PUT ' , url , data = data , * * kwargs )
def patch ( self , url , data = None , * * kwargs ) :
r """ Sends a PATCH request. Returns :class:`HTTPResponse` object.
: arg url : URL to request .
: kwarg data : ( optional ) bytes , or file - like object to send in the body of the request .
: kwarg \* \* kwargs : Optional arguments that ` ` open ` ` takes .
: returns : HTTPResponse
"""
return self . open ( ' PATCH ' , url , data = data , * * kwargs )
def delete ( self , url , * * kwargs ) :
r """ Sends a DELETE request. Returns :class:`HTTPResponse` object.
: arg url : URL to request
: kwargs \* \* kwargs : Optional arguments that ` ` open ` ` takes .
: returns : HTTPResponse
"""
return self . open ( ' DELETE ' , url , * * kwargs )
def open_url ( url , data = None , headers = None , method = None , use_proxy = True ,
force = False , last_mod_time = None , timeout = 10 , validate_certs = True ,
url_username = None , url_password = None , http_agent = None ,
force_basic_auth = False , follow_redirects = ' urllib2 ' ,
client_cert = None , client_key = None , cookies = None ) :
'''
Sends a request via HTTP ( S ) or FTP using urllib2 ( Python2 ) or urllib ( Python3 )
Does not require the module environment
'''
method = method or ' GET '
return Request ( ) . open ( method , url , data = data , headers = headers , use_proxy = use_proxy ,
force = force , last_mod_time = last_mod_time , timeout = timeout , validate_certs = validate_certs ,
url_username = url_username , url_password = url_password , http_agent = http_agent ,
force_basic_auth = force_basic_auth , follow_redirects = follow_redirects ,
client_cert = client_cert , client_key = client_key , cookies = cookies )
r = urllib_request . urlopen ( * urlopen_args )
return r
#
# Module-related functions