Request » History » Version 13
Elmer de Looff, 2012-05-03 10:51
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 | 13 | 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 | 10 | 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 | 1 | Jan Klopper | |
296 | 9 | Elmer de Looff | h1. Environment |
297 | 5 | Elmer de Looff | |
298 | 11 | Elmer de Looff | The request object also has available a number of environment variables. This is a dictionary that exists as the @env@ member of the request object. By default a number of commonly used (or useful) variables will be available, as well as all the HTTP request headers. These latter ones will be prefixed 'HTTP_', and all dashes will be converted to underscores, such that @Content-type@ becomes @HTTP_CONTENT_TYPE@. The other variables are explained below: |
299 | 1 | Jan Klopper | |
300 | 10 | Elmer de Looff | |_.Header |_.Meaning | |
301 | 10 | Elmer de Looff | | CONTENT_LENGTH | The length of the HTTP POST data. Value default to 0 if no POST has been performed. | |
302 | 10 | Elmer de Looff | | CONTENT_TYPE | The content-type of the HTTP POST data, or empty string. | |
303 | 10 | Elmer de Looff | | HTTP_HOST | The host that the client is requesting (e.g. _underdark.nl_). | |
304 | 10 | Elmer de Looff | | HTTP_REFERER | The URL from where people were linked to this page, where provided by the client. | |
305 | 10 | Elmer de Looff | | HTTP_USER_AGENT | Browser identification string of the client. | |
306 | 10 | Elmer de Looff | | PATH_INFO | Path portion of the request URL (e.g. _/admin/login_). | |
307 | 10 | Elmer de Looff | | QUERY_STRING | The raw string of query arguments as received by the server (before parsing, refer to [[Request#Query arguments|query arguments]] for an easier interface). | |
308 | 10 | Elmer de Looff | | REMOTE_ADDR | The client's network address (IPv4 or IPv6, whichever is provided by the underlying system). | |
309 | 10 | Elmer de Looff | | REQUEST_METHOD | The request method. Typically one of @GET@, @HEAD@ or @POST@. | |
310 | 10 | Elmer de Looff | | UWEB_MODE | This is the operational method of µWeb, which is either @STANDALONE@ or @MOD_PYTHON@. | |
311 | 10 | Elmer de Looff | |
312 | 1 | Jan Klopper | h2. Extended environment |
313 | 1 | Jan Klopper | |
314 | 10 | Elmer de Looff | If more information is required, the environment dictionary can be extended with a number of additional points of information. This can be done by calling the @ExtendedEnvironment@ method. This will expand the environment dictionary, and for the benefit of the caller, return it as well. Note that calling this routinely might significantly reduce performance, as extending the environment will among things, perform reverse-DNS lookups. |
315 | 1 | Jan Klopper | |
316 | 10 | Elmer de Looff | The additional values are the following: |
317 | 1 | Jan Klopper | |
318 | 10 | Elmer de Looff | |_.Header |_.Meaning | |
319 | 10 | Elmer de Looff | | DOCUMENT_ROOT | Working directory of the web server (for the given @VirtualHost@ if running on Apache). | |
320 | 10 | Elmer de Looff | | RAW_REQUEST | Raw HTTP request as received by the server, before any parsing. | |
321 | 10 | Elmer de Looff | | REMOTE_HOST | Fully Qualified Domain Name of the client, checked using DNS. | |
322 | 10 | Elmer de Looff | | SERVER_NAME | Local machine name of the computer running the web server. | |
323 | 10 | Elmer de Looff | | SERVER_PORT | Listening port of the web server. | |
324 | 10 | Elmer de Looff | | SERVER_LOCAL_NAME | The FQDN name of the server as known to the web server (Apache @VirtualHost@ affects this). | |
325 | 10 | Elmer de Looff | | SERVER_LOCAL_IP | The local IP that the server runs on. If the server has multiple IPs configured, the one used to connect to the client will be present here. | |
326 | 10 | Elmer de Looff | | SERVER_PROTOCOL | The protocol used for the current connection. Typically @HTTP/1.0@ or @HTTP/1.1@. | |
327 | 10 | Elmer de Looff | |
328 | 10 | Elmer de Looff | h3. Apache-only extended environment |
329 | 10 | Elmer de Looff | |
330 | 12 | Elmer de Looff | Requesting @ExtendedEnvironment@ when µWeb is running on [[Apache]] will also add the following keys to the @env@ dictionary: |
331 | 10 | Elmer de Looff | |
332 | 10 | Elmer de Looff | |_.Header |_.Meaning | |
333 | 10 | Elmer de Looff | | AUTH_TYPE | Authentication that was used. Typically one of @basic@ or @digest@. | |
334 | 10 | Elmer de Looff | | CONNECTION_ID | Connection ID as provided by Apache. | |
335 | 10 | Elmer de Looff | | MODPYTHON_HANDLER | Module name of the [[Request Router]] used for the site. | |
336 | 10 | Elmer de Looff | | MODPYTHON_INTERPRETER | Name of the @VirtualHost@ of the requested site. | |
337 | 10 | Elmer de Looff | | MODPYTHON_PHASE | Phase that @mod_python@ was in when @ExtendedEnvironment@ was called. This typically is @PythonHandler@ | |
338 | 10 | Elmer de Looff | | REMOTE_USER | Username of the authenticated user (available only when using HTTP Authentication). | |