EiffelWeb


Nav: Workbook :: Handling Requests: Form/Query parameters :: Generating Responses

Handling Requests: Headers

Introduction

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.

Table of Contents

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.

Prerequisites

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:

Reading HTTP Header fields

EWF WSF_REQUEST class provides features to access HTTP headers.

Reading most headers is straightforward by calling:

Note: always check if the result of those functions is non-void before using it.

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).

Retrieve information from the Request Line

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

Understanding HTTP 1.1 Request Headers

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.

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.

Building a Table of All Request Headers

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

How to compress pages

To be completed.

Detecting Browser Types

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

Internet Explorer

<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>

Chrome

<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