cubicweb/wsgi/request.py
changeset 11057 0b59724cb3f2
parent 10739 27f946cfe350
child 11154 221febada1e8
equal deleted inserted replaced
11052:058bb3dc685f 11057:0b59724cb3f2
       
     1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     3 #
       
     4 # This file is part of CubicWeb.
       
     5 #
       
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
       
     7 # terms of the GNU Lesser General Public License as published by the Free
       
     8 # Software Foundation, either version 2.1 of the License, or (at your option)
       
     9 # any later version.
       
    10 #
       
    11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT
       
    12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
       
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
       
    14 # details.
       
    15 #
       
    16 # You should have received a copy of the GNU Lesser General Public License along
       
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
       
    18 """WSGI request adapter for cubicweb
       
    19 
       
    20 NOTE: each docstring tagged with ``COME FROM DJANGO`` means that
       
    21 the code has been taken (or adapted) from Djanco source code :
       
    22   http://www.djangoproject.com/
       
    23 
       
    24 """
       
    25 
       
    26 __docformat__ = "restructuredtext en"
       
    27 
       
    28 import tempfile
       
    29 
       
    30 from io import BytesIO
       
    31 
       
    32 from six.moves.urllib.parse import parse_qs
       
    33 
       
    34 from cubicweb.multipart import (
       
    35     copy_file, parse_form_data, parse_options_header)
       
    36 from cubicweb.web import RequestError
       
    37 from cubicweb.web.request import CubicWebRequestBase
       
    38 from cubicweb.wsgi import pformat, normalize_header
       
    39 
       
    40 
       
    41 class CubicWebWsgiRequest(CubicWebRequestBase):
       
    42     """most of this code COMES FROM DJANGO
       
    43     """
       
    44 
       
    45     def __init__(self, environ, vreg):
       
    46         # self.vreg is used in get_posted_data, which is called before the
       
    47         # parent constructor.
       
    48         self.vreg = vreg
       
    49 
       
    50         self.environ = environ
       
    51         self.path = environ['PATH_INFO']
       
    52         self.method = environ['REQUEST_METHOD'].upper()
       
    53 
       
    54         # content_length "may be empty or absent"
       
    55         try:
       
    56             length = int(environ['CONTENT_LENGTH'])
       
    57         except (KeyError, ValueError):
       
    58             length = 0
       
    59         # wsgi.input is not seekable, so copy the request contents to a temporary file
       
    60         if length < 100000:
       
    61             self.content = BytesIO()
       
    62         else:
       
    63             self.content = tempfile.TemporaryFile()
       
    64         copy_file(environ['wsgi.input'], self.content, maxread=length)
       
    65         self.content.seek(0, 0)
       
    66         environ['wsgi.input'] = self.content
       
    67 
       
    68         headers_in = dict((normalize_header(k[5:]), v) for k, v in self.environ.items()
       
    69                           if k.startswith('HTTP_'))
       
    70         if 'CONTENT_TYPE' in environ:
       
    71             headers_in['Content-Type'] = environ['CONTENT_TYPE']
       
    72         https = self.is_secure()
       
    73         if self.path.startswith('/https/'):
       
    74             self.path = self.path[6:]
       
    75             self.environ['PATH_INFO'] = self.path
       
    76             https = True
       
    77 
       
    78         post, files = self.get_posted_data()
       
    79 
       
    80         super(CubicWebWsgiRequest, self).__init__(vreg, https, post,
       
    81                                                   headers= headers_in)
       
    82         self.content = environ['wsgi.input']
       
    83         if files is not None:
       
    84             for key, part in files.items():
       
    85                 self.form[key] = (part.filename, part.file)
       
    86 
       
    87     def __repr__(self):
       
    88         # Since this is called as part of error handling, we need to be very
       
    89         # robust against potentially malformed input.
       
    90         form = pformat(self.form)
       
    91         meta = pformat(self.environ)
       
    92         return '<CubicWebWsgiRequest\FORM:%s,\nMETA:%s>' % \
       
    93             (form, meta)
       
    94 
       
    95     ## cubicweb request interface ################################################
       
    96 
       
    97     def http_method(self):
       
    98         """returns 'POST', 'GET', 'HEAD', etc."""
       
    99         return self.method
       
   100 
       
   101     def relative_path(self, includeparams=True):
       
   102         """return the normalized path of the request (ie at least relative
       
   103         to the instance's root, but some other normalization may be needed
       
   104         so that the returned path may be used to compare to generated urls
       
   105 
       
   106         :param includeparams:
       
   107            boolean indicating if GET form parameters should be kept in the path
       
   108         """
       
   109         path = self.environ['PATH_INFO']
       
   110         path = path[1:] # remove leading '/'
       
   111         if includeparams:
       
   112             qs = self.environ.get('QUERY_STRING')
       
   113             if qs:
       
   114                 return '%s?%s' % (path, qs)
       
   115 
       
   116         return path
       
   117 
       
   118     ## wsgi request helpers ###################################################
       
   119 
       
   120     def is_secure(self):
       
   121         return self.environ['wsgi.url_scheme'] == 'https'
       
   122 
       
   123     def get_posted_data(self):
       
   124         # The WSGI spec says 'QUERY_STRING' may be absent.
       
   125         post = parse_qs(self.environ.get('QUERY_STRING', ''))
       
   126         files = None
       
   127         if self.method == 'POST':
       
   128             content_type = self.environ.get('CONTENT_TYPE')
       
   129             if not content_type:
       
   130                 raise RequestError("Missing Content-Type")
       
   131             content_type, options = parse_options_header(content_type)
       
   132             if content_type in (
       
   133                     'multipart/form-data',
       
   134                     'application/x-www-form-urlencoded',
       
   135                     'application/x-url-encoded'):
       
   136                 forms, files = parse_form_data(
       
   137                     self.environ, strict=True,
       
   138                     mem_limit=self.vreg.config['max-post-length'])
       
   139                 post.update(forms.dict)
       
   140         self.content.seek(0, 0)
       
   141         return post, files
       
   142 
       
   143     def setup_params(self, params):
       
   144         # This is a copy of CubicWebRequestBase.setup_params, but without
       
   145         # converting unicode strings because it is partially done by
       
   146         # get_posted_data
       
   147         self.form = {}
       
   148         if params is None:
       
   149             return
       
   150         encoding = self.encoding
       
   151         for param, val in params.items():
       
   152             if isinstance(val, (tuple, list)):
       
   153                 if len(val) == 1:
       
   154                     val = val[0]
       
   155             if param in self.no_script_form_params and val:
       
   156                 val = self.no_script_form_param(param, val)
       
   157             if param == '_cwmsgid':
       
   158                 self.set_message_id(val)
       
   159             else:
       
   160                 self.form[param] = val