Over a million developers have joined DZone.

Python: Transmitter. A Convenience Class To Simplify HTTP POST Handling.

·
I make no claims for quality of code :)


import httplib
try:
	import logging
	LOGGINGENABLED = True
except ImportError:
	LOGGINGENABLED = False

DOLOGGING = True

def log(s):
	if DOLOGGING:
		if LOGGINGENABLED:
			logging.info(s)
		else:
			print unicode(s)

class BaseTransmitter(object):
	"""Base class for all transmitters.
	
		By itself, it does nothing. Subclass this and use inherited addItem to inject parameters.
		Call transmit() when done.
		
		See classes BasicParam and BinaryParam, which are used with addItem"""
		
	def __init__(self):
		super(BaseTransmitter, self).__init__()
		self.items = []
		self.boundary = '------------ThIs_Is_tHe_bouNdaRY_$' # Kudos!

	def addItem(self, item):
		"""Adds an parameter to be sent in the request.
		
			The added item _must_ define a property 'part' that is a list of strings
			ending with a CRLF (\r\n). Do not include the boundary marker (handled
			in BaseTransmitter.transmit())
			
			See BasicParam and FileParam for supported use."""
			
		self.items.append(item)

	def transmit(self, host, selector, port=80):
		"""Sends request to specified host/selector on default port 80.
		
			Checks that there are items to send, otherwise raises an ValueError exception.
			
			Returns a tuple consisting of:
			- Returned document
			- server response code (e.g. '200' if all goes well)
			- server response string corresponding to response code
			- any RFC822 headers in the response from the server"""
			
		if len(self.items) < 1:
			raise ValueError, "Transmitter contains no items, will not send empty request."
		
		l = []
		CRLF = "\r\n"
		for item in self.items:
			l.append("--" + self.boundary + CRLF)
			l.extend(item.part)
		l.append("--" + self.boundary + CRLF)
		l.append('' + CRLF)
		body = ''.join(l)
		h = httplib.HTTP(host, port)
		h.putrequest('POST', selector)
		h.putheader('content-type', 'multipart/form-data; boundary=%s' % self.boundary)
		h.putheader('content-length', str(len(body)))
		h.putheader('host', host)
		h.endheaders()

		log("Transmitting to %s:%s %s" % (host, port, selector))
		h.send(body)
		errcode, errmsg, headers = h.getreply()

		return h.file.read(), errcode, errmsg, headers

class BasicParam(object):
	"""A basic parameter, i.e. key/value pair"""
	def __init__(self, key, value):
		super(BasicParam, self).__init__()
		
		if not value:
			value=""
		
		L=[]
		L.append('Content-Disposition: form-data; name="%s"' % key.encode("iso-8859-1"))
		L.append('')
		L.append(value.encode("iso-8859-1"))
		L.append('')
		self.part = '\r\n'.join(L)
		log("BasicParam %s added" % key)

class FileParam(object):
	"""A file parameter
	
		Specify the key used in the request, the absolute path to the file and optional
		content type for the fragment.
		
		Checks that the file exists and is actually a file, or raises an IOError."""
		
	def __init__(self, key, absolutefile, contentType="application/octet-stream"):
		import os.path
		super(FileParam, self).__init__()
		if not os.path.exists(absolutefile):
			raise IOError, 'Referenced file "%s" is invalid/nonexistent' % absolutefile
		if not os.path.isfile(absolutefile):
			raise IOError, 'Path does not point to a file (directory?)'

		import os.path
		f = open(absolutefile,'rb')
		L=[]
		L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key.encode("iso-8859-1"), os.path.basename(absolutefile).encode("iso-8859-1")))
		L.append('Content-Type: ' + contentType)
		L.append('')
		L.append(f.read())
		L.append('')
		self.part = '\r\n'.join(L)
		log("FileParam %s added" % key)

class Base64Param(object):
	"""A base64 parameter contained in value. This will NOT be sent as a 'file'"""
	def __init__(self, key, value):
		import base64
		
		super(Base64Param, self).__init__()
		L=[]
		L.append('Content-Disposition: form-data; name="%s"' % key.encode("iso-8859-1"))
		L.append('')
		L.append(base64.encodestring(value))
		L.append('')
		self.part = '\r\n'.join(L)
		log("Base64Param %s added" % key)

class BinaryParam(object):
	"""A binary parameter which is contained in value. Filename will be set to YYYYMMDD_HHMMSS"""
	def __init__(self, key, value, contentType="application/octet-stream"):	
		import time
		
		super(BinaryParam, self).__init__()
		L=[]
		L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key.encode("iso-8859-1"),time.strftime("%Y%m%d_%H%M%S")))
		L.append('Content-Type: ' + contentType)
		L.append('')
		L.append(value)
		L.append('')
		self.part = '\r\n'.join(L)
		log("BinaryParam %s added" % key)


Simple usage:


#!/usr/bin/python
from Transmitter import *

class LogTransmitter(BaseTransmitter):
	def __init__(self, host, paramName, paramValue, port=80):
		super(LogTransmitter, self).__init__()
		self.host = host
		self.selector = "/endpoint/"
		self.port = port

		self.addItem(BasicParam(paramName, paramValue ))

	def transmit(self):
		return super(LogTransmitter, self).transmit(self.host, self.selector, self.port)

t = LogTransmitter("domain.net", "myParam", "myValue")
print repr(t.transmit())
Topics:

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}