Project

General

Profile

Request » History » Version 18

Elmer de Looff, 2013-09-23 14:22
typofix

1 15 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@).
2 1 Jan Klopper
3 17 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 parameters, post data, cookies and environment data. It is also the object where you define cookies that need to be provided to the client.
4 15 Elmer de Looff
5 15 Elmer de Looff
{{toc}}
6 1 Jan Klopper
7 17 Elmer de Looff
h1. Query parameters
8 4 Elmer de Looff
9 17 Elmer de Looff
All query parameters 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:
10 4 Elmer de Looff
11 5 Elmer de Looff
<pre><code class="html">
12 5 Elmer de Looff
...
13 5 Elmer de Looff
<form>
14 5 Elmer de Looff
  <label for="name">Name: </label><input id="name" name="name" />
15 5 Elmer de Looff
  <input type="submit" value="Tell us your name" />
16 5 Elmer de Looff
</form>
17 5 Elmer de Looff
...
18 5 Elmer de Looff
</code></pre>
19 5 Elmer de Looff
20 1 Jan Klopper
<pre><code class="python">
21 5 Elmer de Looff
def NameFromQuery(self):
22 4 Elmer de Looff
  # Retrieves the 'name' argument from the request object:
23 4 Elmer de Looff
  name = self.req.vars['get'].getfirst('name')
24 4 Elmer de Looff
25 4 Elmer de Looff
  # Retrieves the 'name' argument directly from the PageMaker instance (linked to the request):
26 4 Elmer de Looff
  name = self.get.getfirst('name')
27 4 Elmer de Looff
  return name
28 4 Elmer de Looff
</code></pre>
29 4 Elmer de Looff
30 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. 
31 4 Elmer de Looff
32 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:
33 1 Jan Klopper
34 5 Elmer de Looff
<pre><code class="html">
35 5 Elmer de Looff
...
36 5 Elmer de Looff
<form action="/group">
37 5 Elmer de Looff
  <h2>Names in this group</h2>
38 5 Elmer de Looff
  <!-- These would likely be generated with Javascript, but written here for demonstrative purposes -->
39 5 Elmer de Looff
  <label for="name_1">Name: </label><input id="name_1" name="name" />
40 5 Elmer de Looff
  <label for="name_2">Name: </label><input id="name_2" name="name" />
41 5 Elmer de Looff
  <label for="name_3">Name: </label><input id="name_3" name="name" />
42 5 Elmer de Looff
  <input type="submit" value="Send these names" />
43 5 Elmer de Looff
</form>
44 5 Elmer de Looff
...
45 5 Elmer de Looff
</code></pre>
46 5 Elmer de Looff
47 1 Jan Klopper
<pre><code class="python">
48 5 Elmer de Looff
def MemberNames(self):
49 1 Jan Klopper
  names = self.get.getlist('name')
50 1 Jan Klopper
  return ', '.join(names)
51 1 Jan Klopper
</code></pre>
52 1 Jan Klopper
53 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.
54 1 Jan Klopper
55 5 Elmer de Looff
h1. Post data
56 1 Jan Klopper
57 17 Elmer de Looff
Submitted form data is available on the request object as well. The interface is similar to that of the query parameters, 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:
58 1 Jan Klopper
59 5 Elmer de Looff
<pre><code class="html">
60 5 Elmer de Looff
...
61 5 Elmer de Looff
<form method="post">
62 5 Elmer de Looff
  <label for="name">Name: </label><input id="name" name="name" />
63 5 Elmer de Looff
  <input type="submit" value="Tell us your name" />
64 5 Elmer de Looff
</form>
65 5 Elmer de Looff
...
66 5 Elmer de Looff
</code></pre>
67 1 Jan Klopper
68 5 Elmer de Looff
<pre><code class="python">
69 5 Elmer de Looff
def NameFromPost(self):
70 5 Elmer de Looff
  # Retrieves the 'name' value from the request object:
71 5 Elmer de Looff
  name = self.req.vars['post'].getfirst('name')
72 1 Jan Klopper
73 5 Elmer de Looff
  # Retrieves the 'name' value directly from the PageMaker instance (linked to the request):
74 5 Elmer de Looff
  name = self.post.getfirst('name')
75 5 Elmer de Looff
  return name
76 5 Elmer de Looff
</code></pre>
77 1 Jan Klopper
78 17 Elmer de Looff
Like with the query parameters, @getfirst@ accepts a second argument that provides a default other than @None@.
79 1 Jan Klopper
80 17 Elmer de Looff
Multiple values are again possible in the FieldStorage, and these work similar to how they do in query parameters:
81 1 Jan Klopper
82 6 Elmer de Looff
<pre><code class="html">
83 6 Elmer de Looff
...
84 6 Elmer de Looff
<form action="/group" method="post">
85 6 Elmer de Looff
  <h2>Names in this group</h2>
86 6 Elmer de Looff
  <!-- These would likely be generated with Javascript, but written here for demonstrative purposes -->
87 6 Elmer de Looff
  <label for="name_1">Name: </label><input id="name_1" name="name" />
88 6 Elmer de Looff
  <label for="name_2">Name: </label><input id="name_2" name="name" />
89 6 Elmer de Looff
  <label for="name_3">Name: </label><input id="name_3" name="name" />
90 6 Elmer de Looff
  <input type="submit" value="Send these names" />
91 6 Elmer de Looff
</form>
92 6 Elmer de Looff
...
93 6 Elmer de Looff
</code></pre>
94 6 Elmer de Looff
95 6 Elmer de Looff
<pre><code class="python">
96 6 Elmer de Looff
def MemberNames(self):
97 6 Elmer de Looff
  names = self.post.getlist('name')
98 6 Elmer de Looff
  return ', '.join(names)
99 6 Elmer de Looff
</code></pre>
100 6 Elmer de Looff
101 5 Elmer de Looff
h2. Uploading files
102 1 Jan Klopper
103 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.
104 6 Elmer de Looff
105 6 Elmer de Looff
<pre><code class="html">
106 6 Elmer de Looff
...
107 6 Elmer de Looff
<form method="post" enctype="multipart/form-data">
108 6 Elmer de Looff
  <label for="avatar">Avatar: </label><input id="avatar" name="avatar" type="file" />
109 6 Elmer de Looff
  <input type="submit" value="submit!" />
110 6 Elmer de Looff
</form>
111 6 Elmer de Looff
...
112 6 Elmer de Looff
</code></pre>
113 6 Elmer de Looff
114 6 Elmer de Looff
<pre><code class="python">
115 6 Elmer de Looff
def UpdateAvatar(self):
116 6 Elmer de Looff
  # Retrieve the currently logged-in user
117 6 Elmer de Looff
  user = self.GetCurrentUser()
118 6 Elmer de Looff
119 6 Elmer de Looff
  # This gets the name of the file that was uploaded
120 6 Elmer de Looff
  avatar_name = self.post['avatar'].filename
121 6 Elmer de Looff
122 6 Elmer de Looff
  # This retrieves the content of the uploaded file, 
123 6 Elmer de Looff
  avatar_data = self.post['avatar'].value
124 6 Elmer de Looff
125 6 Elmer de Looff
  self.SaveAvatar(user, avatar_data)
126 6 Elmer de Looff
  return 'Your avatar has been replaced by %r' % avatar_name
127 6 Elmer de Looff
</code></pre>
128 6 Elmer de Looff
129 1 Jan Klopper
h2. Structured data using POST
130 6 Elmer de Looff
131 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@:
132 6 Elmer de Looff
133 6 Elmer de Looff
<pre><code class="html">
134 6 Elmer de Looff
...
135 6 Elmer de Looff
<form method="post">
136 6 Elmer de Looff
  <label for="name">Name: </label><input id="name" name="person[name]" />
137 6 Elmer de Looff
  <label for="age">Age: </label><input id="age" name="person[age]" />
138 6 Elmer de Looff
  <label for="job">Job: </label><input id="job" name="person[job]" />
139 6 Elmer de Looff
  <input type="submit" value="Update your profile" />
140 6 Elmer de Looff
</form>
141 6 Elmer de Looff
...
142 6 Elmer de Looff
</code></pre>
143 6 Elmer de Looff
144 6 Elmer de Looff
<pre><code class="python">
145 6 Elmer de Looff
def PersonalData(self):
146 6 Elmer de Looff
  person = self.post.getfirst('person')
147 6 Elmer de Looff
  return uweb.Response(json.dumps(person), content_type="application/json")
148 6 Elmer de Looff
</code></pre>
149 6 Elmer de Looff
150 18 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|response]].
151 6 Elmer de Looff
152 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:
153 6 Elmer de Looff
<pre><code class="python">
154 6 Elmer de Looff
{'age': '28', 'job': 'Engineer', 'name': 'Elmer'}
155 6 Elmer de Looff
</code></pre>
156 6 Elmer de Looff
157 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.
158 5 Elmer de Looff
159 5 Elmer de Looff
h1. Cookies
160 5 Elmer de Looff
161 7 Elmer de Looff
h2. Reading cookies
162 1 Jan Klopper
163 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).
164 7 Elmer de Looff
165 7 Elmer de Looff
The cookie storage itself is a plain Python dictionary, which makes for particularly easy access. 
166 7 Elmer de Looff
167 7 Elmer de Looff
<pre><code class="python">
168 7 Elmer de Looff
def CookieInfo(self):
169 7 Elmer de Looff
  sample = self.cookies['sample']
170 7 Elmer de Looff
  return 'The sample cookie is set to %r' % sample
171 7 Elmer de Looff
</code></pre>
172 7 Elmer de Looff
173 7 Elmer de Looff
Cookies cannot be set by using this dictionary though, for that the @AddCookie@ method is required:
174 7 Elmer de Looff
175 7 Elmer de Looff
h2. Setting cookies
176 7 Elmer de Looff
177 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:
178 7 Elmer de Looff
179 7 Elmer de Looff
<pre><code class="python">
180 7 Elmer de Looff
def SetCookie(self):
181 7 Elmer de Looff
  self.req.AddCookie('example', 'this is an example cookie value set by µWeb')
182 7 Elmer de Looff
  return 'A cookie named "example" was set.'
183 7 Elmer de Looff
</code></pre>
184 7 Elmer de Looff
185 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:
186 7 Elmer de Looff
187 7 Elmer de Looff
<pre><code class="python">
188 7 Elmer de Looff
def ShortLivedCookie(self):
189 7 Elmer de Looff
  """Sets an expiry time of the cookie, in this case 10 seconds."""
190 7 Elmer de Looff
  self.req.AddCookie('quick', 'I will be gone soon', max_age=10)
191 7 Elmer de Looff
192 7 Elmer de Looff
193 7 Elmer de Looff
def SecureCookie(self):
194 7 Elmer de Looff
  """Sets a cookie with the 'secure' flag enabled.
195 7 Elmer de Looff
196 7 Elmer de Looff
  This means the cookie will only be provided with requests that the browser
197 7 Elmer de Looff
  considers secure. This typically means they will only be present in requests
198 7 Elmer de Looff
  that use SSL (https://).
199 7 Elmer de Looff
  """
200 7 Elmer de Looff
  self.req.AddCookie('secret', 'This server adores you', secure=True)
201 7 Elmer de Looff
202 7 Elmer de Looff
203 7 Elmer de Looff
def HttpOnlyCookie(self):
204 7 Elmer de Looff
  """Sets a cookie that is only transferred in HTTP requests.
205 7 Elmer de Looff
206 7 Elmer de Looff
  The cookie will not be readable from Javascript. This defaults to False.
207 7 Elmer de Looff
  """
208 7 Elmer de Looff
  self.req.AddCookie('secret', 'Please no Javascript', httponly=True)
209 7 Elmer de Looff
210 7 Elmer de Looff
211 7 Elmer de Looff
def PathBoundCookie(self):
212 7 Elmer de Looff
  """Sets a cookie that is is only valid for the path '/admin'.
213 7 Elmer de Looff
214 7 Elmer de Looff
  This means that the client (browser) will only provide it for requests
215 7 Elmer de Looff
  that go to '/admin' or a deeper nested path (such as '/admin/users'
216 7 Elmer de Looff
  but will not be provided for requests that go to '/blog'
217 7 Elmer de Looff
  """
218 7 Elmer de Looff
  self.req.AddCookie('user', 'bobbytables', path='/login')
219 7 Elmer de Looff
220 7 Elmer de Looff
221 7 Elmer de Looff
def DomainBoundCookie(self):
222 7 Elmer de Looff
  """Sets a cookie that is is only valid for the specified domain.
223 7 Elmer de Looff
224 7 Elmer de Looff
  By default, if a cookie is set for 'www.example.com' it will not be provided
225 7 Elmer de Looff
  for requests that go to 'example.com' itself. If we set the cookie to be valid
226 7 Elmer de Looff
  for '.domain.com', it will be valid for domain.com and all sub-domains.
227 7 Elmer de Looff
228 7 Elmer de Looff
  Explicitly specified domains MUST begin with a dot, or they will be rejected
229 7 Elmer de Looff
  as per RFC2109. Additionally, cookies set by 'x.y.example.com' MAY NOT set
230 7 Elmer de Looff
  their valid domain to be '.example.com' or they will be rejected.
231 7 Elmer de Looff
  
232 7 Elmer de Looff
  If the 'domain' is not specified, the cookie will be valid for the domain that
233 7 Elmer de Looff
  set the cookie (as per HTTP_HOST from the environment)
234 7 Elmer de Looff
  """
235 7 Elmer de Looff
  self.req.AddCookie('session', 'SMqfUYLk3vCjkWL6', domain='.example.com')
236 7 Elmer de Looff
</code></pre>
237 7 Elmer de Looff
238 8 Elmer de Looff
h1. Headers
239 1 Jan Klopper
240 9 Elmer de Looff
h2. Incoming headers
241 9 Elmer de Looff
242 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.
243 9 Elmer de Looff
244 9 Elmer de Looff
<pre><code class="python">
245 9 Elmer de Looff
def Headers(self):
246 9 Elmer de Looff
  # Hostname that the client (browser) requested:
247 9 Elmer de Looff
  host = self.req.headers['host']
248 9 Elmer de Looff
249 9 Elmer de Looff
  # Retrieves the user-agent from the request
250 9 Elmer de Looff
  user_agent = self.req.headers.get('user-agent', 'unknown')
251 9 Elmer de Looff
252 9 Elmer de Looff
  return 'The host %r was visited by the user-agent identified as %r.' % (host, user_agent)
253 9 Elmer de Looff
</code></pre>
254 9 Elmer de Looff
255 9 Elmer de Looff
h2. Outgoing headers
256 9 Elmer de Looff
257 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.*
258 9 Elmer de Looff
259 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:
260 9 Elmer de Looff
261 9 Elmer de Looff
<pre><code class="python">
262 9 Elmer de Looff
def TaggedResponse(self, content):
263 9 Elmer de Looff
  self.req.AddHeader('ETag', hashlib.sha1(conten).hexdigest())
264 9 Elmer de Looff
  return content
265 9 Elmer de Looff
</code></pre>
266 9 Elmer de Looff
267 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. 
268 9 Elmer de Looff
269 9 Elmer de Looff
h3. Content-type
270 9 Elmer de Looff
271 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:
272 9 Elmer de Looff
273 9 Elmer de Looff
<pre><code class="python">
274 9 Elmer de Looff
def CustomContent(self):
275 9 Elmer de Looff
  with file('lolcat.jpg') as image:
276 9 Elmer de Looff
    self.req.SetContentType('image/jpeg')
277 9 Elmer de Looff
    return image.read()
278 9 Elmer de Looff
</code></pre>
279 9 Elmer de Looff
280 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._
281 9 Elmer de Looff
282 9 Elmer de Looff
h3. HTTP Response code
283 9 Elmer de Looff
284 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:
285 9 Elmer de Looff
286 9 Elmer de Looff
<pre><code class="python">
287 9 Elmer de Looff
def FourOhFour(self, path):
288 9 Elmer de Looff
  self.req.SetHttpCode(404)
289 9 Elmer de Looff
  return "Sorry, we don't have a page that looks like %r" % path
290 9 Elmer de Looff
</code></pre>
291 9 Elmer de Looff
292 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._
293 1 Jan Klopper
294 9 Elmer de Looff
h1. Environment
295 5 Elmer de Looff
296 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:
297 1 Jan Klopper
298 10 Elmer de Looff
|_.Header |_.Meaning |
299 10 Elmer de Looff
| CONTENT_LENGTH | The length of the HTTP POST data. Value default to 0 if no POST has been performed. |
300 10 Elmer de Looff
| CONTENT_TYPE | The content-type of the HTTP POST data, or empty string. |
301 10 Elmer de Looff
| HTTP_HOST | The host that the client is requesting (e.g. _underdark.nl_). |
302 10 Elmer de Looff
| HTTP_REFERER | The URL from where people were linked to this page, where provided by the client. |
303 10 Elmer de Looff
| HTTP_USER_AGENT | Browser identification string of the client. |
304 16 Elmer de Looff
| PATH_INFO | Path portion of the request URL (e.g. _/admin/login_). If high bytes (anything beyond ASCII) are provided by the client, µWeb attempts to decode these from UTF8, resulting in a @unicode@ object. If this fails, the raw string is used. |
305 17 Elmer de Looff
| QUERY_STRING | The raw string of query parameters as received by the server (before parsing, refer to [[Request#Query parameters|query parameters]] for an easier interface). |
306 10 Elmer de Looff
| REMOTE_ADDR | The client's network address (IPv4 or IPv6, whichever is provided by the underlying system). |
307 10 Elmer de Looff
| REQUEST_METHOD | The request method. Typically one of @GET@, @HEAD@ or @POST@. |
308 10 Elmer de Looff
| UWEB_MODE | This is the operational method of µWeb, which is either @STANDALONE@ or @MOD_PYTHON@. |
309 10 Elmer de Looff
310 1 Jan Klopper
h2. Extended environment
311 1 Jan Klopper
312 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.
313 1 Jan Klopper
314 10 Elmer de Looff
The additional values are the following:
315 1 Jan Klopper
316 10 Elmer de Looff
|_.Header |_.Meaning |
317 10 Elmer de Looff
| DOCUMENT_ROOT | Working directory of the web server (for the given @VirtualHost@ if running on Apache). |
318 10 Elmer de Looff
| RAW_REQUEST | Raw HTTP request as received by the server, before any parsing. |
319 10 Elmer de Looff
| REMOTE_HOST | Fully Qualified Domain Name of the client, checked using DNS. |
320 10 Elmer de Looff
| SERVER_NAME | Local machine name of the computer running the web server. |
321 10 Elmer de Looff
| SERVER_PORT | Listening port of the web server. |
322 10 Elmer de Looff
| SERVER_LOCAL_NAME | The FQDN name of the server as known to the web server (Apache @VirtualHost@ affects this). |
323 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. |
324 10 Elmer de Looff
| SERVER_PROTOCOL | The protocol used for the current connection. Typically @HTTP/1.0@ or @HTTP/1.1@. |
325 10 Elmer de Looff
326 10 Elmer de Looff
h3. Apache-only extended environment
327 10 Elmer de Looff
328 12 Elmer de Looff
Requesting @ExtendedEnvironment@ when µWeb is running on [[Apache]] will also add the following keys to the @env@ dictionary:
329 10 Elmer de Looff
330 10 Elmer de Looff
|_.Header |_.Meaning |
331 10 Elmer de Looff
| AUTH_TYPE | Authentication that was used. Typically one of @basic@ or @digest@. |
332 10 Elmer de Looff
| CONNECTION_ID | Connection ID as provided by Apache. |
333 10 Elmer de Looff
| MODPYTHON_HANDLER | Module name of the [[Request Router]] used for the site. |
334 10 Elmer de Looff
| MODPYTHON_INTERPRETER | Name of the @VirtualHost@ of the requested site. |
335 10 Elmer de Looff
| MODPYTHON_PHASE | Phase that @mod_python@ was in when @ExtendedEnvironment@ was called. This typically is @PythonHandler@ |
336 10 Elmer de Looff
| REMOTE_USER | Username of the authenticated user (available only when using HTTP Authentication). |