Nav: Workbook :: Handling Requests: Form/Query parameters :: Generating Responses
Form Data
.A request usually includes the header fields Accept, Accept-Encoding, Connection, Cookie, Host, Referer, and User-Agent, defining important information about how the server should process the request. And then, the server needs to read the request header fields to use those informations.
That section explains how to read HTTP information sent by the browser via the request header fields. Mostly by defining the most important HTTP request header fields, for more information, read HTTP 1.1 specification.
The Eiffel Web Framework is using the traditional Common Gateway Interface (CGI) programming interface to access the header fields, query and form parameters. Among other, this means the header fields are exposed with associated CGI field names:
CONTENT_TYPE
and CONTENT_LENGTH
.X-Server
will be known as HTTP_X_SERVER
.EWF WSF_REQUEST
class provides features to access HTTP headers.
Reading most headers is straightforward by calling:
http_*
functions such as http_accept
for header “Accept”.meta_string_variable (a_name)
function by passing the associated CGI field name.
In both cases, if the related header field is supplied by the request, the result is a string value, otherwise it is Void.Note: always check if the result of those functions is non-void before using it.
cookies: ITERABLE [WSF_VALUE]
cookie (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
auth_type: detachable READABLE_STRING_8
http_authorization: detachable READABLE_STRING_8 --Contents of the Authorization: header from the current wgi_request, if there is one.
content_length: detachable READABLE_STRING_8
content_length_value: NATURAL_64
content_type: detachable HTTP_CONTENT_TYPE
Due to CGI compliance, the original header names are not available, however the function raw_header_data
may return the http header data as a string value (warning: this may not be available, depending on the underlying connector). Apart from very specific cases (proxy, debugging, …), it should not be useful.
Note: CGI variables are information about the current request (and also about the server). Some are based on the HTTP request line and headers (e.g., form parameters, query parameters), others are derived from the socket itself (e.g., the name and IP address of the requesting host), and still others are taken from server installation parameters (e.g., the mapping of URLs to actual paths).
For convenience, the following sections refer to a request starting with line:
GET http://eiffel.org/search?q=EiffelBase HTTP/1.1
Overview of the features
request_method: READABLE_STRING_8
gives access to the HTTP request method, (usually GET
or POST
in conventional Web Applications), but with the raise of REST APIs other methods are also frequently used such as HEAD
, PUT
, DELETE
, OPTIONS
, or TRACE
.
A few functions helps determining quickly the nature of the request method:is_get_request_method: BOOLEAN -- Is Current a GET request method?
is_put_request_method: BOOLEAN -- Is Current a PUT request method?
is_post_request_method: BOOLEAN -- Is Current a POST request method?
is_delete_request_method: BOOLEAN -- Is Current a DELETE request method?
In our example the request method is GET
Query String
- The query string for the example is q=EiffelBase
- query_string: READABLE_STRING_8
server_protocol: READABLE_STRING_8
In the example the request method is HTTP/1.1
Access to the request headers permits the web server applications or APIs to perform optimizations and provide behavior that would not be possible without them for instance such as adapting the response according to the browser preferences. This section summarizes the headers most often used; for more information, see the HTTP 1.1 specification, note that RFC 2616 is dead.
Content-Negotiation
gzip
, compress
) are acceptable in the response. An “identity” token is used as a synonym for “no encoding” in order to communicate when no encoding is preferred. If the server receives this header, it is free to encode the page by using one of the content-encodings specified (usually to reduce transmission time), sending the Content-Encoding
response header to indicate that it has done so.Generally, If-Modified-Since is used for GET requests (“give me the document only if it is newer than my cached version”), whereas If-Unmodified-Since is used for PUT requests (“update this document only if nobody else has changed it since I generated it”).
For example, if you are at Web page A and click on a link to Web page B, the URL of Web page A is included in the Referer header when the browser requests Web page B.
Note: the example shows the WSF_EXECUTION implementation, that will be used by the service launcher.
The following EWF service code simply uses an html_template
to fill a table (names and values) with all the headers fields it receives.
The service accomplishes this task by calling req.meta_variables
feature to get an ITERABLE [WSF_STRING]
, an structure that can be iterated over using across...loop...end
, then it checks if the name has the prefix HTTP_
and if it is true, put the header name and value in a row. (the name in the left cell, the value in the right cell).
The service also writes three components of the main request line (method, URI, and protocol), and also the raw header.
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the incomming request
local
l_raw_data: STRING
l_page_response: STRING
l_rows: STRING
do
create l_page_response.make_from_string (html_template)
if req.path_info.same_string ("/") then
-- HTTP method
l_page_response.replace_substring_all ("$http_method", req.request_method)
-- URI
l_page_response.replace_substring_all ("$uri", req.path_info)
-- Protocol
l_page_response.replace_substring_all ("$protocol", req.server_protocol)
-- Fill the table rows with HTTP Headers
create l_rows.make_empty
across req.meta_variables as ic loop
if ic.item.name.starts_with ("HTTP_") then
l_rows.append ("<tr>")
l_rows.append ("<td>")
l_rows.append (ic.item.name)
l_rows.append ("</td>")
l_rows.append ("<td>")
l_rows.append (ic.item.value)
l_rows.append ("</td>")
l_rows.append ("</tr>")
end
end
l_page_response.replace_substring_all ("$rows", l_rows)
-- Reading the raw header
if attached req.raw_header_data as l_raw_header then
l_page_response.replace_substring_all ("$raw_header", l_raw_header)
end
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_page_response.count.out]>>)
res.put_string (l_page_response)
end
end
html_template: STRING = "[
<!DOCTYPE html>
<html>
<head>
<style>
thead {color:green;}
tbody {color:blue;}
table, th, td {
border: 1px solid black;
}
</style>
</head>
<body>
<h1>EWF service example: Showing Request Headers</h1>
<strong>HTTP METHOD:</strong>$http_method<br/>
<strong>URI:</strong>$uri<br>
<strong>PROTOCOL:</strong>$protocol<br/>
<strong>REQUEST TIME:</strong>$time<br/>
<br>
<table>
<thead>
<tr>
<th>Header Name</th>
<th>Header Value</th>
</tr>
</thead>
<tbody>
$rows
</tbody>
</table>
<h2>Raw header</h2>
$raw_header
</body>
</html>
]"
end
To be completed.
The User-Agent header identifies the specific browser/client that is sending the request. The following code shows an EWF service that sends browser-specific responses.
The examples uses the ideas based on the Browser detection using the user agent article.
Basically the code check if the header user_agent
exist and then call the browser_name (a_user_agent: READABLE_STRING_8): READABLE_STRING_32
feature to retrieve the current browser name or Unknown in other case.
class
APPLICATION_EXECUTION
inherit
WSF_EXECUTION
create
make
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the incomming request
local
l_raw_data: STRING
l_page_response: STRING
l_rows: STRING
do
create l_page_response.make_from_string (html_template)
if req.path_info.same_string ("/") then
-- retrieve the user-agent
if attached req.http_user_agent as l_user_agent then
l_page_response.replace_substring_all ("$user_agent", l_user_agent)
l_page_response.replace_substring_all ("$browser", browser_name (l_user_agent))
else
l_page_response.replace_substring_all ("$user_agent", "[]")
l_page_response.replace_substring_all ("$browser", "Unknown, the user-agent was not present.")
end
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_page_response.count.out]>>)
res.put_string (l_page_response)
end
end
feature -- Browser utility
browser_name (a_user_agent: READABLE_STRING_8): READABLE_STRING_32
-- Browser name.
-- Must contain Must not contain
-- Firefox Firefox/xyz Seamonkey/xyz
-- Seamonkey Seamonkey/xyz
-- Chrome Chrome/xyz Chromium/xyz
-- Chromium Chromium/xyz
-- Safari Safari/xyz Chrome/xyz
-- Chromium/xyz
-- Opera OPR/xyz [1]
-- Opera/xyz [2]
-- Internet Explorer ;MSIE xyz; Internet Explorer doesn't put its name in the BrowserName/VersionNumber format
do
if
a_user_agent.has_substring ("Firefox") and then
not a_user_agent.has_substring ("Seamonkey")
then
Result := "Firefox"
elseif a_user_agent.has_substring ("Seamonkey") then
Result := "Seamonkey"
elseif a_user_agent.has_substring ("Chrome") and then not a_user_agent.has_substring ("Chromium")then
Result := "Chrome"
elseif a_user_agent.has_substring ("Chromium") then
Result := "Chromiun"
elseif a_user_agent.has_substring ("Safari") and then not (a_user_agent.has_substring ("Chrome") or else a_user_agent.has_substring ("Chromium")) then
Result := "Safari"
elseif a_user_agent.has_substring ("OPR") or else a_user_agent.has_substring ("Opera") then
Result := "Opera"
elseif a_user_agent.has_substring ("MSIE") or else a_user_agent.has_substring ("Trident")then
Result := "Internet Explorer"
else
Result := "Unknown"
end
end
html_template: STRING = "[
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>EWF service example: Showing Browser Dectection Using User-Agent</h1> <br>
<strong>User Agent:</strong> $user_agent <br>
<h2>Enjoy using $browser </h2>
</body>
</html>
]"
end
Let see some results, we will show the html returned
<h1>EWF service example: Showing Browser Dectection Using User-Agent</h1></br>
<strong>User Agent:</strong> Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; MDDCJS; rv:11.0) like Gecko <br>
<h2> Enjoy using Internet Explorer </h2>
<h1>EWF service example: Showing Browser Dectection Using User-Agent</h1></br>
<strong>User Agent:</strong> Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.91 Safari/537.36 <br>
<h2> Enjoy using Chrome </h2>
As an exercise, try to write a similar service to retrieve the OS family using the User-Agent information.
Meta-variables contains data about the request, they are identified by case-insensitive names. In this section, the purpose is to show a similar example to HEADERS FIELDS, but in this case building a table showing the standard CGI variables.
* [AUTH_TYPE](https://tools.ietf.org/html/rfc3875#section-4.1.1).
* [CONTENT_LENGTH](https://tools.ietf.org/html/rfc3875#section-4.1.2)
* [CONTENT_TYPE](https://tools.ietf.org/html/rfc3875#section-4.1.3)
* [GATEWAY_INTERFACE](https://tools.ietf.org/html/rfc3875#section-4.1.4)
* [PATH_INFO](https://tools.ietf.org/html/rfc3875#section-4.1.5)
* [PATH_TRANSLATED](https://tools.ietf.org/html/rfc3875#section-4.1.6)
* [QUERY_STRING](https://tools.ietf.org/html/rfc3875#section-4.1.7)
* [REMOTE_ADDR](https://tools.ietf.org/html/rfc3875#section-4.1.8)
* [REMOTE_HOST](https://tools.ietf.org/html/rfc3875#section-4.1.9)
* [REMOTE_IDENT](https://tools.ietf.org/html/rfc3875#section-4.1.10)
* [REMOTE_USER](https://tools.ietf.org/html/rfc3875#section-4.1.11)
* [REQUEST_METHOD](https://tools.ietf.org/html/rfc3875#section-4.1.12)
* [SCRIPT_NAME](https://tools.ietf.org/html/rfc3875#section-4.1.13)
* [SERVER_NAME](https://tools.ietf.org/html/rfc3875#section-4.1.14)
* [SERVER_PROTOCOL](https://tools.ietf.org/html/rfc3875#section-4.1.15)
* [SERVER_SOFTWARE](https://tools.ietf.org/html/rfc3875#section-4.1.16)
Example An EWF service that shows the CGI variables, creates a table showing the values of all the CGI variables.
Nav: Workbook :: Handling Requests: Form/Query parameters :: Generating Responses