wsgi/request.py
changeset 9821 2077c8da1893
parent 9755 566d90bdc565
parent 9735 b71158815bc8
child 9990 c84ad981fc4a
equal deleted inserted replaced
9819:95902c0b991b 9821:2077c8da1893
    25 
    25 
    26 __docformat__ = "restructuredtext en"
    26 __docformat__ = "restructuredtext en"
    27 
    27 
    28 from StringIO import StringIO
    28 from StringIO import StringIO
    29 from urllib import quote
    29 from urllib import quote
       
    30 from urlparse import parse_qs
    30 
    31 
    31 from logilab.common.decorators import cached
    32 from cubicweb.multipart import copy_file, parse_form_data
    32 
       
    33 from cubicweb.web.request import CubicWebRequestBase
    33 from cubicweb.web.request import CubicWebRequestBase
    34 from cubicweb.wsgi import (pformat, qs2dict, safe_copyfileobj, parse_file_upload,
    34 from cubicweb.wsgi import pformat, normalize_header
    35                            normalize_header)
       
    36 from cubicweb.web.http_headers import Headers
       
    37 
       
    38 
    35 
    39 
    36 
    40 class CubicWebWsgiRequest(CubicWebRequestBase):
    37 class CubicWebWsgiRequest(CubicWebRequestBase):
    41     """most of this code COMES FROM DJANGO
    38     """most of this code COMES FROM DJANGO
    42     """
    39     """
    43 
    40 
    44     def __init__(self, environ, vreg):
    41     def __init__(self, environ, vreg):
    45         self.environ = environ
    42         self.environ = environ
    46         self.path = environ['PATH_INFO']
    43         self.path = environ['PATH_INFO']
    47         self.method = environ['REQUEST_METHOD'].upper()
    44         self.method = environ['REQUEST_METHOD'].upper()
       
    45 
       
    46         # content_length "may be empty or absent"
    48         try:
    47         try:
    49             length = int(environ['CONTENT_LENGTH'])
    48             length = int(environ['CONTENT_LENGTH'])
    50         except (KeyError, ValueError):
    49         except (KeyError, ValueError):
    51             length = 0
    50             length = 0
    52         # wsgi.input is not seekable, so copy the request contents to a temporary file
    51         # wsgi.input is not seekable, so copy the request contents to a temporary file
    53         if length < 100000:
    52         if length < 100000:
    54             self.content = StringIO()
    53             self.content = StringIO()
    55         else:
    54         else:
    56             self.content = tempfile.TemporaryFile()
    55             self.content = tempfile.TemporaryFile()
    57         safe_copyfileobj(environ['wsgi.input'], self.content, size=length)
    56         copy_file(environ['wsgi.input'], self.content, maxread=length)
    58         self.content.seek(0, 0)
    57         self.content.seek(0, 0)
       
    58         environ['wsgi.input'] = self.content
    59 
    59 
    60         headers_in = dict((normalize_header(k[5:]), v) for k, v in self.environ.items()
    60         headers_in = dict((normalize_header(k[5:]), v) for k, v in self.environ.items()
    61                           if k.startswith('HTTP_'))
    61                           if k.startswith('HTTP_'))
    62         https = self.is_secure()
    62         https = self.is_secure()
    63         post, files = self.get_posted_data()
    63         post, files = self.get_posted_data()
    64 
    64 
    65         super(CubicWebWsgiRequest, self).__init__(vreg, https, post,
    65         super(CubicWebWsgiRequest, self).__init__(vreg, https, post,
    66                                                   headers= headers_in)
    66                                                   headers= headers_in)
    67         if files is not None:
    67         if files is not None:
    68             for key, (name, _, stream) in files.iteritems():
    68             for key, part in files.iteritems():
    69                 if name is not None:
    69                 name = None
    70                     name = unicode(name, self.encoding)
    70                 if part.filename is not None:
    71                 self.form[key] = (name, stream)
    71                     name = unicode(part.filename, self.encoding)
       
    72                 self.form[key] = (name, part.file)
    72 
    73 
    73     def __repr__(self):
    74     def __repr__(self):
    74         # Since this is called as part of error handling, we need to be very
    75         # Since this is called as part of error handling, we need to be very
    75         # robust against potentially malformed input.
    76         # robust against potentially malformed input.
    76         form = pformat(self.form)
    77         form = pformat(self.form)
   106     def is_secure(self):
   107     def is_secure(self):
   107         return self.environ['wsgi.url_scheme'] == 'https'
   108         return self.environ['wsgi.url_scheme'] == 'https'
   108 
   109 
   109     def get_posted_data(self):
   110     def get_posted_data(self):
   110         # The WSGI spec says 'QUERY_STRING' may be absent.
   111         # The WSGI spec says 'QUERY_STRING' may be absent.
   111         post = qs2dict(self.environ.get('QUERY_STRING', ''))
   112         post = parse_qs(self.environ.get('QUERY_STRING', ''))
   112         files = None
   113         files = None
   113         if self.method == 'POST':
   114         if self.method == 'POST':
   114             if self.environ.get('CONTENT_TYPE', '').startswith('multipart'):
   115             forms, files = parse_form_data(self.environ, strict=True,
   115                 header_dict = dict((normalize_header(k[5:]), v)
   116                                            mem_limit=self.vreg.config['max-post-length'])
   116                                    for k, v in self.environ.items()
   117             post.update(forms)
   117                                    if k.startswith('HTTP_'))
   118         self.content.seek(0, 0)
   118                 header_dict['Content-Type'] = self.environ.get('CONTENT_TYPE', '')
       
   119                 post_, files = parse_file_upload(header_dict, self.raw_post_data)
       
   120                 post.update(post_)
       
   121             else:
       
   122                 post.update(qs2dict(self.raw_post_data))
       
   123         return post, files
   119         return post, files
   124 
       
   125     @property
       
   126     @cached
       
   127     def raw_post_data(self):
       
   128         postdata = self.content.read()
       
   129         self.content.seek(0, 0)
       
   130         return postdata