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 |
|