wsgi/request.py
changeset 9735 b71158815bc8
parent 9563 48f0ff3e2a32
child 9821 2077c8da1893
child 9939 46a8ed48636f
equal deleted inserted replaced
9729:1fe9dad662e5 9735:b71158815bc8
    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 = environ.get("HTTPS") in ('yes', 'on', '1')
    62         https = environ.get("HTTPS") in ('yes', 'on', '1')
    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)
   130         return 'wsgi.url_scheme' in self.environ \
   131         return 'wsgi.url_scheme' in self.environ \
   131             and self.environ['wsgi.url_scheme'] == 'https'
   132             and self.environ['wsgi.url_scheme'] == 'https'
   132 
   133 
   133     def get_posted_data(self):
   134     def get_posted_data(self):
   134         # The WSGI spec says 'QUERY_STRING' may be absent.
   135         # The WSGI spec says 'QUERY_STRING' may be absent.
   135         post = qs2dict(self.environ.get('QUERY_STRING', ''))
   136         post = parse_qs(self.environ.get('QUERY_STRING', ''))
   136         files = None
   137         files = None
   137         if self.method == 'POST':
   138         if self.method == 'POST':
   138             if self.environ.get('CONTENT_TYPE', '').startswith('multipart'):
   139             forms, files = parse_form_data(self.environ, strict=True,
   139                 header_dict = dict((normalize_header(k[5:]), v)
   140                                            mem_limit=self.vreg.config['max-post-length'])
   140                                    for k, v in self.environ.items()
   141             post.update(forms)
   141                                    if k.startswith('HTTP_'))
   142         self.content.seek(0, 0)
   142                 header_dict['Content-Type'] = self.environ.get('CONTENT_TYPE', '')
       
   143                 post_, files = parse_file_upload(header_dict, self.raw_post_data)
       
   144                 post.update(post_)
       
   145             else:
       
   146                 post.update(qs2dict(self.raw_post_data))
       
   147         return post, files
   143         return post, files
   148 
       
   149     @property
       
   150     @cached
       
   151     def raw_post_data(self):
       
   152         postdata = self.content.read()
       
   153         self.content.seek(0, 0)
       
   154         return postdata