Package pyrobase :: Package io :: Module http
[hide private]
[frames] | no frames]

Source Code for Module pyrobase.io.http

  1  # -*- coding: utf-8 -*- 
  2  # pylint: disable=too-few-public-methods 
  3  """ HTTP support. 
  4   
  5      Copyright (c) 2012 The PyroScope Project <pyroscope.project@gmail.com> 
  6  """ 
  7  # This program is free software; you can redistribute it and/or modify 
  8  # it under the terms of the GNU General Public License as published by 
  9  # the Free Software Foundation; either version 2 of the License, or 
 10  # (at your option) any later version. 
 11  # 
 12  # This program is distributed in the hope that it will be useful, 
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 15  # GNU General Public License for more details. 
 16  # 
 17  # You should have received a copy of the GNU General Public License along 
 18  # with this program; if not, write to the Free Software Foundation, Inc., 
 19  # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 20   
 21  import os 
 22  import sys 
 23  import time 
 24  import httplib 
 25  import urlparse 
 26  import StringIO 
 27  import mimetypes 
 28   
 29  from pyrobase import fmt, parts 
 30   
 31   
32 -class HttpPost(object):
33 """ Do a HTTP multipart/form-data POST. 34 """ 35
36 - def __init__(self, url, fields, headers=None, mock_http=False):
37 """ Initialize POST data. 38 39 Field values can be strings or files; files 40 are expected to have a read() method and SHOULD have a 'name' 41 attribute (i.e. look like handles returned by 'open()'). 42 43 @param url: the URL to POST to. 44 @param fields: sequence of (name, value) tuples. 45 @param headers: dict of additional headers. 46 """ 47 self.url = url 48 self.fields = fields 49 self.headers = headers or {} 50 self.mock_http = mock_http
51 52
53 - def __repr__(self):
54 """ Show POST data. 55 """ 56 # TODO: headers 57 return '\n'.join([ 58 "POST to '%s' with these fields:" % (self.url,), 59 ] + [ 60 " %s=%r" % i for i in self.fields 61 ])
62 63
64 - def send(self):
65 """ Post fields and files to an HTTP server as multipart/form-data. 66 Return the server's response. 67 """ 68 scheme, location, path, query, _ = urlparse.urlsplit(self.url) 69 assert scheme in ("http", "https"), "Unsupported scheme %r" % scheme 70 71 content_type, body = self._encode_multipart_formdata() 72 handle = getattr(httplib, scheme.upper() + "Connection")(location) 73 if self.mock_http: 74 # Don't actually send anything, print to stdout instead 75 handle.sock = parts.Bunch( 76 sendall=lambda x: sys.stdout.write(fmt.to_utf8( 77 ''.join((c if 32 <= ord(c) < 127 or ord(c) in (8, 10) else u'\u27ea%02X\u27eb' % ord(c)) for c in x) 78 )), 79 makefile=lambda dummy, _: StringIO.StringIO("\r\n".join(( 80 "HTTP/1.0 204 NO CONTENT", 81 "Content-Length: 0", 82 "", 83 ))), 84 close=lambda: None, 85 ) 86 87 handle.putrequest('POST', urlparse.urlunsplit(('', '', path, query, ''))) 88 handle.putheader('Content-Type', content_type) 89 handle.putheader('Content-Length', str(len(body))) 90 for key, val in self.headers.items(): 91 handle.putheader(key, val) 92 handle.endheaders() 93 handle.send(body) 94 #print handle.__dict__ 95 96 return handle.getresponse()
97 98
100 """ Encode POST body. 101 Return (content_type, body) ready for httplib.HTTP instance 102 """ 103 def get_content_type(filename): 104 "Helper to get MIME type." 105 return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
106 107 boundary = '----------ThIs_Is_tHe_b0uNdaRY_%d$' % (time.time()) 108 logical_lines = [] 109 for name, value in self.fields: 110 if value is None: 111 continue 112 logical_lines.append('--' + boundary) 113 if hasattr(value, "read"): 114 filename = getattr(value, "name", str(id(value))+".dat") 115 logical_lines.append('Content-Disposition: form-data; name="%s"; filename="%s"' % ( 116 name, 117 os.path.basename(filename).replace("'", '_').replace('"', '_') 118 )) 119 logical_lines.append('Content-Type: %s' % get_content_type(filename)) 120 logical_lines.append('Content-Transfer-Encoding: binary') 121 value = value.read() 122 else: 123 logical_lines.append('Content-Disposition: form-data; name="%s"' % name) 124 logical_lines.append('Content-Type: text/plain; charset="UTF-8"') 125 value = fmt.to_utf8(value) 126 #logical_lines.append('Content-Length: %d' % len(value)) 127 logical_lines.append('') 128 logical_lines.append(value) 129 logical_lines.append('--' + boundary + '--') 130 logical_lines.append('') 131 132 body = '\r\n'.join(logical_lines) 133 content_type = 'multipart/form-data; boundary=%s' % boundary 134 return content_type, body
135