Project

General

Profile

Request » History » Version 11

Elmer de Looff, 2012-05-01 17:32

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 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 10 Elmer de Looff
Requesting @ExtendedEnvironment@ 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). |