Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
173 lines (148 sloc) 5.79 KB
# Copyright (c) 2013
# - Zachary Cutlip <uid000@gmail.com>
# - Tactical Network Solutions, LLC
#
# See LICENSE.txt for more details.
#
import urllib
import urllib2
import mimetools
import itertools
import os
from urllib2 import HTTPError
from ..common.support import Logging
class HttpClient(object):
"""
A very basic HTTP client class whose primary purpose is sending a string
to a server via POST or GET, and to abstract the details of
"""
def encode(self,data):
"""
If you need to control what data is URL encoded, use this, otherwise set
urlencode=True on send()
"""
if type(data) == dict:
data=urllib.urlencode(data)
else:
data=urllib.quote_plus(data)
return data
def send(self,url,headers=None,post_data=None,urlencode=False,get_resp=True,debug_req=False):
"""
Send HTTP data. If post data is provided, the request is sent as a POST,
otherwise it is sent as a GET.
Note: For POST requests, Content-Length header is not calculated. It
be provided in the headers parameter.
Parameters
----------
url: URL to send the GET or POST to.
headers: Optional. A dictionary of header key/value pairs describing
the HTTP headers to send. Ordering is not guaranteed.
post_data: Optional. Data to send in a POST request. Defaults to None.
urlencode: Optional. URL encode the POST data. Defaults to False.
get_resp: Optional. A response is expected, and will be read() and
returned. Defaults to true.
debug_req: Optional. Set debug level on the URL opener so that the
request is printed to the console.
"""
if post_data and urlencode:
post_data=self.encode(post_data)
if post_data and headers:
req = urllib2.Request(url,post_data,headers)
elif post_data:
req = urllib2.Request(url,post_data)
elif headers:
req = urllib2.Request(url,None,headers)
else:
req = urllib2.Request(url)
#Instead of using urlib2.urlopen(),
#Create a HTTPHandler object and optionally set its debug level to 1
httpHandler = urllib2.HTTPHandler()
if debug_req:
httpHandler.set_http_debuglevel(1)
#Instead of using urllib2.urlopen, create an opener,
#and pass the HTTPHandler and any other handlers... to it.
opener = urllib2.build_opener(httpHandler)
#Use your opener to open the Request.
response = opener.open(req)
#response = urllib2.urlopen(req)
resp_data=None
if get_resp:
resp_data = response.read()
return resp_data
class MultipartForm(object):
"""
A class to generate a multipart/form-data body for use with a POST request.
Generate the multipart form string then send as a POST with the HttpClient
class.
Shamelessly ripped off from: http://pymotw.com/2/urllib2/
"""
def __init__(self):
self.form_fields=[]
self.files=[]
self.boundary=mimetools.choose_boundary()
def get_content_type(self):
"""
Return the content-type, including multipart boundary, for this object.
"""
return "multipart/form-data; boundary=%s" % self.boundary
def add_field(self,name,value):
"""
Add a simple form field to the form data.
Parameters
----------
name: Name of the field to add.
value: Value of the field to add.
"""
self.form_fields.append((name,value))
def add_file(self,fieldname,filename,filename_override=None,mimetype=None):
"""
Add a file to be uploaded.
Parameters
----------
fieldname: Textual field name for the form being submitted.
filename: Path and name of file on disk to open, read, and include in
the form data.
filename_override: Optional. Override the filename with this string.
Otherwise the basename of the filename parameter
will be used in the form data.
mimetype: Mimetype to specify for the attached file. If none, defaults
to: 'application/octet-stream'
"""
body=open(filename,"rb").read()
file_basename=""
if filename_override != None:
file_basename=filename_override
else:
file_basename=os.path.basename(filename)
if mimetype == None:
mimetype='application/octet-stream'
self.files.append((fieldname,file_basename,mimetype,body))
def __str__(self):
"""
Get a string representation of the form data suitable for use as
post_data parameter to HttpClient.send()
"""
parts=[]
part_boundary = "--" + self.boundary
parts.extend(
[ part_boundary,
'Content-Disposition: form-data; name="%s"' % name,
'',
value,
]
for name,value in self.form_fields
)
parts.extend(
[ part_boundary,
'Content-Disposition: file; name="%s"; filename="%s"' % \
(field_name,filename),
'Content-Type: %s' % content_type,
'',
body,
]
for field_name,filename,content_type,body in self.files
)
flattened = list(itertools.chain(*parts))
flattened.append('--' + self.boundary + '--')
flattened.append('')
return '\r\n'.join(flattened)