Request » History » Version 9
Elmer de Looff, 2012-05-01 16:35
Setting and reading headers using Request
1 | 1 | Jan Klopper | h1. Request |
---|---|---|---|
2 | 1 | Jan Klopper | |
3 | 5 | Elmer de Looff | {{>toc}} |
4 | 5 | Elmer de Looff | |
5 | 3 | Elmer de Looff | The @Request@ object is an abstraction of the incoming HTTP request. This allows one simple interface that is independent of the underlying server that µWeb runs on (either [[Standalone]] using BaseHTTPServer, or [[Apache]] mode on @mod_python@). |
6 | 1 | Jan Klopper | |
7 | 4 | Elmer de Looff | From PageMaker methods, the request object is accessible as the @self.req@ member. The request object contains all the information about the incoming request: query arguments, post data, cookies and environment data. It is also the object where you define cookies that need to be provided to the client. |
8 | 1 | Jan Klopper | |
9 | 4 | Elmer de Looff | h1. Query arguments |
10 | 4 | Elmer de Looff | |
11 | 4 | Elmer de Looff | All query arguments provided by the client are present on the request object. They are also accessible directly on the [[PageMaker]] object. The following code demonstrates both ways to access a query argument: |
12 | 4 | Elmer de Looff | |
13 | 5 | Elmer de Looff | <pre><code class="html"> |
14 | 5 | Elmer de Looff | ... |
15 | 5 | Elmer de Looff | <form> |
16 | 5 | Elmer de Looff | <label for="name">Name: </label><input id="name" name="name" /> |
17 | 5 | Elmer de Looff | <input type="submit" value="Tell us your name" /> |
18 | 5 | Elmer de Looff | </form> |
19 | 5 | Elmer de Looff | ... |
20 | 5 | Elmer de Looff | </code></pre> |
21 | 5 | Elmer de Looff | |
22 | 1 | Jan Klopper | <pre><code class="python"> |
23 | 5 | Elmer de Looff | def NameFromQuery(self): |
24 | 4 | Elmer de Looff | # Retrieves the 'name' argument from the request object: |
25 | 4 | Elmer de Looff | name = self.req.vars['get'].getfirst('name') |
26 | 4 | Elmer de Looff | |
27 | 4 | Elmer de Looff | # Retrieves the 'name' argument directly from the PageMaker instance (linked to the request): |
28 | 4 | Elmer de Looff | name = self.get.getfirst('name') |
29 | 4 | Elmer de Looff | return name |
30 | 4 | Elmer de Looff | </code></pre> |
31 | 4 | Elmer de Looff | |
32 | 4 | Elmer de Looff | Using the @getfirst@ method, you get a single string returned from the query argument mapping, or a @None@ if no such value exists. Much like a dictionary's @get@ method, you can provide a second argument to the method, and have that returned instead as the default. |
33 | 4 | Elmer de Looff | |
34 | 1 | Jan Klopper | Now, HTTP allows the client to provide the same query argument multiple times. Using @getfirst@ you would only get the very first defined argument. So a request that looks like @http://example.org/group?name=Bob&name=Mark&name=Jenny@ would only return 'Bob' in the previous example. To get all their names printed, you can use the following: |
35 | 1 | Jan Klopper | |
36 | 5 | Elmer de Looff | <pre><code class="html"> |
37 | 5 | Elmer de Looff | ... |
38 | 5 | Elmer de Looff | <form action="/group"> |
39 | 5 | Elmer de Looff | <h2>Names in this group</h2> |
40 | 5 | Elmer de Looff | <!-- These would likely be generated with Javascript, but written here for demonstrative purposes --> |
41 | 5 | Elmer de Looff | <label for="name_1">Name: </label><input id="name_1" name="name" /> |
42 | 5 | Elmer de Looff | <label for="name_2">Name: </label><input id="name_2" name="name" /> |
43 | 5 | Elmer de Looff | <label for="name_3">Name: </label><input id="name_3" name="name" /> |
44 | 5 | Elmer de Looff | <input type="submit" value="Send these names" /> |
45 | 5 | Elmer de Looff | </form> |
46 | 5 | Elmer de Looff | ... |
47 | 5 | Elmer de Looff | </code></pre> |
48 | 5 | Elmer de Looff | |
49 | 1 | Jan Klopper | <pre><code class="python"> |
50 | 5 | Elmer de Looff | def MemberNames(self): |
51 | 1 | Jan Klopper | names = self.get.getlist('name') |
52 | 1 | Jan Klopper | return ', '.join(names) |
53 | 1 | Jan Klopper | </code></pre> |
54 | 1 | Jan Klopper | |
55 | 1 | Jan Klopper | This returns a neat comma-separated string with all the provided names. The @getlist@ method does not take a default, but will instead return an empty list when there are no values for the requested argument name. |
56 | 1 | Jan Klopper | |
57 | 5 | Elmer de Looff | h1. Post data |
58 | 1 | Jan Klopper | |
59 | 5 | Elmer de Looff | Submitted form data is available on the request object as well. The interface is similar to that of the query arguments, and the @FieldStorage@ class already present in the @cgi@ module. If we take our initial example form handler, but now receive the data through HTTP POST, the code would look like this: |
60 | 1 | Jan Klopper | |
61 | 5 | Elmer de Looff | <pre><code class="html"> |
62 | 5 | Elmer de Looff | ... |
63 | 5 | Elmer de Looff | <form method="post"> |
64 | 5 | Elmer de Looff | <label for="name">Name: </label><input id="name" name="name" /> |
65 | 5 | Elmer de Looff | <input type="submit" value="Tell us your name" /> |
66 | 5 | Elmer de Looff | </form> |
67 | 5 | Elmer de Looff | ... |
68 | 5 | Elmer de Looff | </code></pre> |
69 | 1 | Jan Klopper | |
70 | 5 | Elmer de Looff | <pre><code class="python"> |
71 | 5 | Elmer de Looff | def NameFromPost(self): |
72 | 5 | Elmer de Looff | # Retrieves the 'name' value from the request object: |
73 | 5 | Elmer de Looff | name = self.req.vars['post'].getfirst('name') |
74 | 1 | Jan Klopper | |
75 | 5 | Elmer de Looff | # Retrieves the 'name' value directly from the PageMaker instance (linked to the request): |
76 | 5 | Elmer de Looff | name = self.post.getfirst('name') |
77 | 5 | Elmer de Looff | return name |
78 | 5 | Elmer de Looff | </code></pre> |
79 | 1 | Jan Klopper | |
80 | 5 | Elmer de Looff | Like with the query arguments, @getfirst@ accepts a second argument that provides a default other than @None@. |
81 | 1 | Jan Klopper | |
82 | 6 | Elmer de Looff | Multiple values are again possible in the FieldStorage, and these work similar to how they do in query arguments: |
83 | 1 | Jan Klopper | |
84 | 6 | Elmer de Looff | <pre><code class="html"> |
85 | 6 | Elmer de Looff | ... |
86 | 6 | Elmer de Looff | <form action="/group" method="post"> |
87 | 6 | Elmer de Looff | <h2>Names in this group</h2> |
88 | 6 | Elmer de Looff | <!-- These would likely be generated with Javascript, but written here for demonstrative purposes --> |
89 | 6 | Elmer de Looff | <label for="name_1">Name: </label><input id="name_1" name="name" /> |
90 | 6 | Elmer de Looff | <label for="name_2">Name: </label><input id="name_2" name="name" /> |
91 | 6 | Elmer de Looff | <label for="name_3">Name: </label><input id="name_3" name="name" /> |
92 | 6 | Elmer de Looff | <input type="submit" value="Send these names" /> |
93 | 6 | Elmer de Looff | </form> |
94 | 6 | Elmer de Looff | ... |
95 | 6 | Elmer de Looff | </code></pre> |
96 | 6 | Elmer de Looff | |
97 | 6 | Elmer de Looff | <pre><code class="python"> |
98 | 6 | Elmer de Looff | def MemberNames(self): |
99 | 6 | Elmer de Looff | names = self.post.getlist('name') |
100 | 6 | Elmer de Looff | return ', '.join(names) |
101 | 6 | Elmer de Looff | </code></pre> |
102 | 6 | Elmer de Looff | |
103 | 5 | Elmer de Looff | h2. Uploading files |
104 | 1 | Jan Klopper | |
105 | 6 | Elmer de Looff | Processing an uploaded file is done using the the same @FieldStorage@ system as the rest of the POST data, and roughly looks like the following. When performing file uploads, be sure to define the @enctype@ of your form, or the uploaded file will have no contents. |
106 | 6 | Elmer de Looff | |
107 | 6 | Elmer de Looff | <pre><code class="html"> |
108 | 6 | Elmer de Looff | ... |
109 | 6 | Elmer de Looff | <form method="post" enctype="multipart/form-data"> |
110 | 6 | Elmer de Looff | <label for="avatar">Avatar: </label><input id="avatar" name="avatar" type="file" /> |
111 | 6 | Elmer de Looff | <input type="submit" value="submit!" /> |
112 | 6 | Elmer de Looff | </form> |
113 | 6 | Elmer de Looff | ... |
114 | 6 | Elmer de Looff | </code></pre> |
115 | 6 | Elmer de Looff | |
116 | 6 | Elmer de Looff | <pre><code class="python"> |
117 | 6 | Elmer de Looff | def UpdateAvatar(self): |
118 | 6 | Elmer de Looff | # Retrieve the currently logged-in user |
119 | 6 | Elmer de Looff | user = self.GetCurrentUser() |
120 | 6 | Elmer de Looff | |
121 | 6 | Elmer de Looff | # This gets the name of the file that was uploaded |
122 | 6 | Elmer de Looff | avatar_name = self.post['avatar'].filename |
123 | 6 | Elmer de Looff | |
124 | 6 | Elmer de Looff | # This retrieves the content of the uploaded file, |
125 | 6 | Elmer de Looff | avatar_data = self.post['avatar'].value |
126 | 6 | Elmer de Looff | |
127 | 6 | Elmer de Looff | self.SaveAvatar(user, avatar_data) |
128 | 6 | Elmer de Looff | return 'Your avatar has been replaced by %r' % avatar_name |
129 | 6 | Elmer de Looff | </code></pre> |
130 | 6 | Elmer de Looff | |
131 | 1 | Jan Klopper | h2. Structured data using POST |
132 | 6 | Elmer de Looff | |
133 | 6 | Elmer de Looff | One of the things that has been extended on the basic @FieldStorage@ in µWeb is the way it treats square backets ( [ and ] ) in POST data. A form field with the name @person[name]@ will result in a dictionary @person@ being created in the resulting @FieldStorage@: |
134 | 6 | Elmer de Looff | |
135 | 6 | Elmer de Looff | <pre><code class="html"> |
136 | 6 | Elmer de Looff | ... |
137 | 6 | Elmer de Looff | <form method="post"> |
138 | 6 | Elmer de Looff | <label for="name">Name: </label><input id="name" name="person[name]" /> |
139 | 6 | Elmer de Looff | <label for="age">Age: </label><input id="age" name="person[age]" /> |
140 | 6 | Elmer de Looff | <label for="job">Job: </label><input id="job" name="person[job]" /> |
141 | 6 | Elmer de Looff | <input type="submit" value="Update your profile" /> |
142 | 6 | Elmer de Looff | </form> |
143 | 6 | Elmer de Looff | ... |
144 | 6 | Elmer de Looff | </code></pre> |
145 | 6 | Elmer de Looff | |
146 | 6 | Elmer de Looff | <pre><code class="python"> |
147 | 6 | Elmer de Looff | def PersonalData(self): |
148 | 6 | Elmer de Looff | person = self.post.getfirst('person') |
149 | 6 | Elmer de Looff | return uweb.Response(json.dumps(person), content_type="application/json") |
150 | 6 | Elmer de Looff | </code></pre> |
151 | 6 | Elmer de Looff | |
152 | 6 | Elmer de Looff | In the above code here, the @person@ variable is a dictionary retrieved from the POST data, which is then presented to the client in JSON, by using a custom [[Response|repsonse]]. |
153 | 6 | Elmer de Looff | |
154 | 6 | Elmer de Looff | Note that the 'numeric' age value is a string. This is of course because everything submitted in forms is in the form of a string. Conversion to appropriate types will have to be handled by the [[PageMaker]]. The @person@ dictionary itself looks like this: |
155 | 6 | Elmer de Looff | <pre><code class="python"> |
156 | 6 | Elmer de Looff | {'age': '28', 'job': 'Engineer', 'name': 'Elmer'} |
157 | 6 | Elmer de Looff | </code></pre> |
158 | 6 | Elmer de Looff | |
159 | 6 | Elmer de Looff | *N.B.:* When using structured form data, you still need to use the @getfirst@ method, because there might me separate (non-dictionary) values for the form name. There will never be more than one dictionary in the form values; if a single key is set more than once, the last-set value will be the one present in the dictionary. |
160 | 5 | Elmer de Looff | |
161 | 5 | Elmer de Looff | h1. Cookies |
162 | 5 | Elmer de Looff | |
163 | 7 | Elmer de Looff | h2. Reading cookies |
164 | 1 | Jan Klopper | |
165 | 7 | Elmer de Looff | Cookies provided by the client will also end up in the request object. They are both present on the request itself, as @self.req.vars['cookies']@, or through the @PageMaker@ instance itself as @self.cookies@ (both are from the scope of the PageMaker instance). |
166 | 7 | Elmer de Looff | |
167 | 7 | Elmer de Looff | The cookie storage itself is a plain Python dictionary, which makes for particularly easy access. |
168 | 7 | Elmer de Looff | |
169 | 7 | Elmer de Looff | <pre><code class="python"> |
170 | 7 | Elmer de Looff | def CookieInfo(self): |
171 | 7 | Elmer de Looff | sample = self.cookies['sample'] |
172 | 7 | Elmer de Looff | return 'The sample cookie is set to %r' % sample |
173 | 7 | Elmer de Looff | </code></pre> |
174 | 7 | Elmer de Looff | |
175 | 7 | Elmer de Looff | Cookies cannot be set by using this dictionary though, for that the @AddCookie@ method is required: |
176 | 7 | Elmer de Looff | |
177 | 7 | Elmer de Looff | h2. Setting cookies |
178 | 7 | Elmer de Looff | |
179 | 7 | Elmer de Looff | Response cookies are set using the request object. The method to use for this is @AddCookie@, the easiest use of which looks like this: |
180 | 7 | Elmer de Looff | |
181 | 7 | Elmer de Looff | <pre><code class="python"> |
182 | 7 | Elmer de Looff | def SetCookie(self): |
183 | 7 | Elmer de Looff | self.req.AddCookie('example', 'this is an example cookie value set by µWeb') |
184 | 7 | Elmer de Looff | return 'A cookie named "example" was set.' |
185 | 7 | Elmer de Looff | </code></pre> |
186 | 7 | Elmer de Looff | |
187 | 7 | Elmer de Looff | This creates a cookie that does not expire, will be provided with every request to the originating domain, and can be read from Javascript. To change these default behaviors, there are a number of optional arguments that can be provided, as detailed below. Of course, while the examples show one argument used at a time, they can all be combined: |
188 | 7 | Elmer de Looff | |
189 | 7 | Elmer de Looff | <pre><code class="python"> |
190 | 7 | Elmer de Looff | def ShortLivedCookie(self): |
191 | 7 | Elmer de Looff | """Sets an expiry time of the cookie, in this case 10 seconds.""" |
192 | 7 | Elmer de Looff | self.req.AddCookie('quick', 'I will be gone soon', max_age=10) |
193 | 7 | Elmer de Looff | |
194 | 7 | Elmer de Looff | |
195 | 7 | Elmer de Looff | def SecureCookie(self): |
196 | 7 | Elmer de Looff | """Sets a cookie with the 'secure' flag enabled. |
197 | 7 | Elmer de Looff | |
198 | 7 | Elmer de Looff | This means the cookie will only be provided with requests that the browser |
199 | 7 | Elmer de Looff | considers secure. This typically means they will only be present in requests |
200 | 7 | Elmer de Looff | that use SSL (https://). |
201 | 7 | Elmer de Looff | """ |
202 | 7 | Elmer de Looff | self.req.AddCookie('secret', 'This server adores you', secure=True) |
203 | 7 | Elmer de Looff | |
204 | 7 | Elmer de Looff | |
205 | 7 | Elmer de Looff | def HttpOnlyCookie(self): |
206 | 7 | Elmer de Looff | """Sets a cookie that is only transferred in HTTP requests. |
207 | 7 | Elmer de Looff | |
208 | 7 | Elmer de Looff | The cookie will not be readable from Javascript. This defaults to False. |
209 | 7 | Elmer de Looff | """ |
210 | 7 | Elmer de Looff | self.req.AddCookie('secret', 'Please no Javascript', httponly=True) |
211 | 7 | Elmer de Looff | |
212 | 7 | Elmer de Looff | |
213 | 7 | Elmer de Looff | def PathBoundCookie(self): |
214 | 7 | Elmer de Looff | """Sets a cookie that is is only valid for the path '/admin'. |
215 | 7 | Elmer de Looff | |
216 | 7 | Elmer de Looff | This means that the client (browser) will only provide it for requests |
217 | 7 | Elmer de Looff | that go to '/admin' or a deeper nested path (such as '/admin/users' |
218 | 7 | Elmer de Looff | but will not be provided for requests that go to '/blog' |
219 | 7 | Elmer de Looff | """ |
220 | 7 | Elmer de Looff | self.req.AddCookie('user', 'bobbytables', path='/login') |
221 | 7 | Elmer de Looff | |
222 | 7 | Elmer de Looff | |
223 | 7 | Elmer de Looff | def DomainBoundCookie(self): |
224 | 7 | Elmer de Looff | """Sets a cookie that is is only valid for the specified domain. |
225 | 7 | Elmer de Looff | |
226 | 7 | Elmer de Looff | By default, if a cookie is set for 'www.example.com' it will not be provided |
227 | 7 | Elmer de Looff | for requests that go to 'example.com' itself. If we set the cookie to be valid |
228 | 7 | Elmer de Looff | for '.domain.com', it will be valid for domain.com and all sub-domains. |
229 | 7 | Elmer de Looff | |
230 | 7 | Elmer de Looff | Explicitly specified domains MUST begin with a dot, or they will be rejected |
231 | 7 | Elmer de Looff | as per RFC2109. Additionally, cookies set by 'x.y.example.com' MAY NOT set |
232 | 7 | Elmer de Looff | their valid domain to be '.example.com' or they will be rejected. |
233 | 7 | Elmer de Looff | |
234 | 7 | Elmer de Looff | If the 'domain' is not specified, the cookie will be valid for the domain that |
235 | 7 | Elmer de Looff | set the cookie (as per HTTP_HOST from the environment) |
236 | 7 | Elmer de Looff | """ |
237 | 7 | Elmer de Looff | self.req.AddCookie('session', 'SMqfUYLk3vCjkWL6', domain='.example.com') |
238 | 7 | Elmer de Looff | </code></pre> |
239 | 7 | Elmer de Looff | |
240 | 8 | Elmer de Looff | h1. Headers |
241 | 1 | Jan Klopper | |
242 | 9 | Elmer de Looff | h2. Incoming headers |
243 | 9 | Elmer de Looff | |
244 | 9 | Elmer de Looff | Request headers are made available in the @headers@ member of the request object. This works like a regular dictionary (though writing to this dictionary is not guaranteed to be successful), where all the keys are in lower-case. The @get@ method works to retrieve the header, with an optional default if the header wasn't provided by the client. |
245 | 9 | Elmer de Looff | |
246 | 9 | Elmer de Looff | <pre><code class="python"> |
247 | 9 | Elmer de Looff | def Headers(self): |
248 | 9 | Elmer de Looff | # Hostname that the client (browser) requested: |
249 | 9 | Elmer de Looff | host = self.req.headers['host'] |
250 | 9 | Elmer de Looff | |
251 | 9 | Elmer de Looff | # Retrieves the user-agent from the request |
252 | 9 | Elmer de Looff | user_agent = self.req.headers.get('user-agent', 'unknown') |
253 | 9 | Elmer de Looff | |
254 | 9 | Elmer de Looff | return 'The host %r was visited by the user-agent identified as %r.' % (host, user_agent) |
255 | 9 | Elmer de Looff | </code></pre> |
256 | 9 | Elmer de Looff | |
257 | 9 | Elmer de Looff | h2. Outgoing headers |
258 | 9 | Elmer de Looff | |
259 | 9 | Elmer de Looff | *While it is possible to provide response headers via the @Request@ object, it is strongly advised to provide them using the @[[Response]]@ object. This generally leads to clearer code, and has less caveats than using the methods laid out below.* |
260 | 9 | Elmer de Looff | |
261 | 9 | Elmer de Looff | Adding headers to outgoing responses can be done using the @AddHeader@ method of the request object. Please note that cookies can be set more easily (as described [[Request#Setting cookies|above]]), as well as creating [[Response#Redirect|redirects]]. Responding with a custom content-type and HTTP status code will be explained below. Setting your own headers, for example to provide ETags, is done like this: |
262 | 9 | Elmer de Looff | |
263 | 9 | Elmer de Looff | <pre><code class="python"> |
264 | 9 | Elmer de Looff | def TaggedResponse(self, content): |
265 | 9 | Elmer de Looff | self.req.AddHeader('ETag', hashlib.sha1(conten).hexdigest()) |
266 | 9 | Elmer de Looff | return content |
267 | 9 | Elmer de Looff | </code></pre> |
268 | 9 | Elmer de Looff | |
269 | 9 | Elmer de Looff | The above example returns a simple ETag based on the "SHA-1 hash":http://en.wikipedia.org/wiki/SHA-1 of the content returned. |
270 | 9 | Elmer de Looff | |
271 | 9 | Elmer de Looff | h3. Content-type |
272 | 9 | Elmer de Looff | |
273 | 9 | Elmer de Looff | The content-type of the reply would usually be configured by returning a custom [[Response]] object. When it is not desirable to use this, the content-type can be set using the @SetContentType@ method of the request object: |
274 | 9 | Elmer de Looff | |
275 | 9 | Elmer de Looff | <pre><code class="python"> |
276 | 9 | Elmer de Looff | def CustomContent(self): |
277 | 9 | Elmer de Looff | with file('lolcat.jpg') as image: |
278 | 9 | Elmer de Looff | self.req.SetContentType('image/jpeg') |
279 | 9 | Elmer de Looff | return image.read() |
280 | 9 | Elmer de Looff | </code></pre> |
281 | 9 | Elmer de Looff | |
282 | 9 | Elmer de Looff | *N.B.:* Note that returning a @Response@ object will override the content-type set on the @request@ object. That is, _returning the image in the example above using @Response@ (without the @content-type@ defined there) will create a response with the default @text/html@ content-type._ |
283 | 9 | Elmer de Looff | |
284 | 9 | Elmer de Looff | h3. HTTP Response code |
285 | 9 | Elmer de Looff | |
286 | 9 | Elmer de Looff | The HTTP response code on the webserver reply can also be set directly on the @request@ object when a full @Response@ object is for any reason not desirable: |
287 | 9 | Elmer de Looff | |
288 | 9 | Elmer de Looff | <pre><code class="python"> |
289 | 9 | Elmer de Looff | def FourOhFour(self, path): |
290 | 9 | Elmer de Looff | self.req.SetHttpCode(404) |
291 | 9 | Elmer de Looff | return "Sorry, we don't have a page that looks like %r" % path |
292 | 9 | Elmer de Looff | </code></pre> |
293 | 9 | Elmer de Looff | |
294 | 9 | Elmer de Looff | *N.B.:* Note that returning a @Response@ object will override the HTTP status code set on the @request@ object. That is, _using a Response object in the example above (without providing the @httpcode@ argument) will return a HTTP 200 OK response._ |
295 | 9 | Elmer de Looff | |
296 | 9 | Elmer de Looff | |
297 | 9 | Elmer de Looff | |
298 | 5 | Elmer de Looff | h1. Environment |
299 | 1 | Jan Klopper | |
300 | 1 | Jan Klopper | The env variable is a dictionary containing the following items; |
301 | 1 | Jan Klopper | * CONTENT_TYPE |
302 | 1 | Jan Klopper | * CONTENT_LENGTH |
303 | 1 | Jan Klopper | * HTTP_COOKIE |
304 | 1 | Jan Klopper | * HTTP_HOST |
305 | 1 | Jan Klopper | * HTTP_REFERER |
306 | 1 | Jan Klopper | * HTTP_USER_AGENT |
307 | 1 | Jan Klopper | * PATH_INFO |
308 | 1 | Jan Klopper | * QUERY_STRING |
309 | 1 | Jan Klopper | * REMOTE_ADDR |
310 | 1 | Jan Klopper | * REQUEST_METHOD |
311 | 1 | Jan Klopper | * UWEB_MODE 'STANDALONE' / 'MOD_PYTHON' |
312 | 1 | Jan Klopper | |
313 | 5 | Elmer de Looff | h2. Extended environment |
314 | 5 | Elmer de Looff | |
315 | 1 | Jan Klopper | If more detail is required about the environment, you can issue a call to the self.req.ExtendedEnvironment() method, which will inject more details into the env var. This is a much slower operation than the normal env call, so that's why its tucked away in a separate method. |
316 | 1 | Jan Klopper | |
317 | 1 | Jan Klopper | * AUTH_TYPE |
318 | 1 | Jan Klopper | * CONNECTION_ID |
319 | 1 | Jan Klopper | * DOCUMENT_ROOT |
320 | 1 | Jan Klopper | * RAW_REQUEST |
321 | 1 | Jan Klopper | * REMOTE_HOST |
322 | 1 | Jan Klopper | * REMOTE_USER |
323 | 1 | Jan Klopper | * SERVER_NAME |
324 | 1 | Jan Klopper | * SERVER_PORT |
325 | 1 | Jan Klopper | * SERVER_LOCAL_NAME |
326 | 1 | Jan Klopper | * SERVER_LOCAL_IP |
327 | 2 | Elmer de Looff | * SERVER_PROTOCOL |
328 | 1 | Jan Klopper | |
329 | 1 | Jan Klopper | And in case of a @mod_python@ setup you will also get: |
330 | 1 | Jan Klopper | * MODPYTHON_HANDLER |
331 | 1 | Jan Klopper | * MODPYTHON_INTERPRETER |
332 | 5 | Elmer de Looff | * MODPYTHON_PHASE |