SEGGER - Jobs
emWin upgrade     Trade-In program     Web shop

embOS/IP Web Server

The embOS/IP Web server is an optional extension which adds the HTTP protocol to the stack. The Web server can be used with embOS/IP or with a different TCP/IP stack. It combines a maximum of performance with a small memory footprint. The Web server allows an embedded system to present Web pages with dynamically generated content. It comes with all features typically required by embedded systems: multiple connections, authentication, forms and low RAM usage. RAM usage has been kept to a minimum by smart buffer handling.

The Web server implements the relevant parts of the following Request For Comments (RFC).

RFC# Description
[RFC 1945] HTTP - Hypertext Transfer Protocol -- HTTP/1.0
Direct download: ftp://ftp.rfc-editor.org/in-notes/rfc1945.txt
[RFC 2616] HTTP - Hypertext Transfer Protocol -- HTTP/1.1
Direct download: ftp://ftp.rfc-editor.org/in-notes/rfc2616.txt

The following table shows the contents of the embOS/IP Web server root directory:

Directory Content
.\Application\ Contains the example application to run the Web server with embOS/IP. The standard application consists of two files. OS_IP_Webserver.c and Webserver_DynContent.c. OS_IP_Webserver.c fits for the most applications without modifications. Webserver_DynContent.c includes the dynamic parts of our sample application, like virtual files, CGI functions, etc.
.\Config\ Contains the Web server configuration file. Refer to Configuration; for detailed information.
.\Inc\ Contains the required include files.
.\IP\ Contains the Web server sources, IP_Webserver.c, IP_Webserver.h and IP_UTIL_BASE64.c, IP_UTIL.h.
.\IP\FS\ Contains the sources for the file system abstraction layer and the read-only file system. Details about the "File system abstraction layer" can be found in the embOS/IP user manual.
.\Windows\Webserver\ Contains the source, the project files and an executable to run embOS/IP Web server on a Microsoft Windows host. Refer to Using the Web server sample for detailed information.

Feature list

  • Low memory footprint.
  • Dynamic Web pages.
  • Authentication supported.
  • Forms: POST and GET support.
  • Multiple connections supported.
  • JavaScript supported.
  • AJAX supported.
  • SSE supported.
  • REST supported.
  • r/o file system included.
  • HTML to C converter included.
  • Independent of the file system: any file system can be used.
  • Independent of the TCP/IP stack: any stack with sockets can be used.
  • Demo with authentication, various forms, dynamic pages included.
  • Project for executable on PC for Microsoft Visual Studio included.

Requirements

TCP/IP stack

The embOS/IP Web server requires a TCP/IP stack. It is optimized for embOS/IP, but any RFC-compliant TCP/IP stack can be used. The shipment includes a Win32 simulation, which uses the standard Winsock API and an implementation which uses the socket API of embOS/IP.

Multi tasking

The Web server needs to run as a separate thread. Therefore, a multi tasking system is required to use the embOS/IP Web server.

HTTP backgrounds

It is a communication protocol originally designed to transfer information via hypertext pages. The development of HTTP is coordinated by the IETF (Internet Engineering Task Force) and the W3C (World Wide Web Consortium). The current protocol version is 1.1.

HTTP communication basics

HTTP is a challenge and response protocol. A client initiates a TCP connection to the Web server and sends a HTTP request. A HTTP request starts with a method token. [RFC 2616] defines 8 method tokens. The method token indicates the method to be performed on the requested resource. embOS/IP Web server supports all methods which are typically required by an embedded Web server.

HTTP method Description
GET The GET method means that it retrieves whatever information is
identified by the Request-URI.
HEAD The HEAD method means that it retrieves the header of the content which is identified by the Request-URI.
POST The POST method submits data to be processed to the identified
resource. The data is included in the body of the request.

The following example shows parts of a HTTP session, where a client (for example, 192.168.1.75) asks the embOS/IP Web server for the hypertext page example.html. The request is followed by a blank line, so that the request ends with a double newline, each in the form of a carriage return followed by a line feed.

GET /example.html HTTP/1.1
Host: 192.168.1.75

The first line of every response message is the Status-Line, consisting of the protocol version followed by a numeric status code. The Status-Line is followed by the content-type, the server, expiration and the transfer-encoding. The server response ends with an empty line, followed by length of content that should be transferred. The length indicates the length of the Web page in bytes.

HTTP/1.1 200 OK
Content-Type: text/html
Server: embOS/IP
Expires: THU, 26 OCT 1995 00:00:00 GMT
Transfer-Encoding: chunked
A3

Thereafter, the Web server sends the requested hypertext page to the client. The zero at the end of the Web page followed by an empty line signalizes that the transmission of the requested Web page is complete.

<html>
  <head>
    <title>embOS/IP examples</title>
  </head>
  <body>
    <center>
      <h1>Website: example.htm</h1>
    </center>
  </body>
</html>
0

HTTP status codes

The first line of a HTTP response is the Status-Line. It consists of the used protocol version, a status code and a short textual description of the Status-Code. The Status-Code element is a 3-digit integer result code of the attempt to understand and satisfy the request.

The first digit of the Status-Code defines the class of response. The last two digits do not have any categorization role. There are 5 values for the first digit:

  • 1xx: Informational - Request received, continuing process.
  • 2xx: Success - The action was successfully received, understood, and accepted.
  • 3xx: Redirection - Further action must be taken in order to complete the request.
  • 4xx: Client Error - The request contains bad syntax or cannot be fulfilled.
  • 5xx: Server Error - The server failed to fulfill an apparently valid request.

Refer to [RFC 2616] for a complete list of defined status-codes. embOS/IP Web server supports a subset of the defined HTTP status codes. The following status codes are implemented:

Status code Description
200 OK. The request has succeeded.
401 Unauthorized. The request requires user authentication.
404 Not found. The server has not found anything matching the
Request-URI.
501 Not implemented. The server does not support the HTTP method.
503 Service unavailable. The server is currently unable to handle the request due to a temporary overloading of the server.

Using the Web server sample

Ready to use examples for Microsoft Windows and embOS/IP are supplied. If you use another TCP/IP stack, the sample OS_IP_Webserver.c has to be adapted.

The Web server itself does not handle multiple connections. This is part of the application and is included in the OS_IP_Webserver.c sample.

The sample application opens a port which listens on port 80 until an incoming connection is detected in a parent task that accepts new connections (or rejects them if no more connections can be accepted).

For each accepted client connection, the parent task creates a child task running IP_WEBS_ProcessEx() in a separated context that will then process the request of the connected client (for example a browser). This way the parent task is ready to handle further incoming connections on port 80.

Therefore the sample uses n client connections + one for the parent task.

Some browsers may open multiple connections and do not even intend to close the connection. They rather keep the connections open for further data that might be requested. To give other clients a chance, a special handling is implemented in the Web server.

The embOS/IP Web server has two functions for processing a connection in a child task:

  • IP_WEBS_ProcessEx(), that allows a connection to stay open even after all data has been sent from the target. The connection will stay open as long as the client does not close it.
  • IP_WEBS_ProcessLastEx(), that will close the connection once the target has sent all data requested. This is used by the Web server sample for the last free connection available. This ensures that at least one connection will be available after it has been served to accept further clients.

In addition to available connections that can be served directly, a feature called "backlogging" can be used.
This means additional connections will be accepted (SYN/ACK is sent from target) but not yet processed. They will be processed as soon as a free connection becomes available once a child task has served the clients request and has been closed.
Connections in backlog will be kept active until the client side sends a reset due to a possible timeout in the client.

The example application uses a read-only file system to make Web pages available. Details about the "File system abstraction layer" can be found in the embOS/IP user manual

Using the Windows sample

If you have MS Visual C++ 6.00 or any later version available, you will be able to work with a Windows sample project using embOS/IP Web server. If you do not have the Microsoft compiler, an precompiled executable of the Web server is also supplied.

Building the sample program

Open the workspace Start_Webserver.dsw with MS Visual Studio (for example, double-clicking it). There is no further configuration necessary. You should be able to build the application without any error or warning message.

The server uses the IP address of the host PC on which it runs. Open a Web browser and connect by entering the IP address of the host (127.0.0.1) to connect to the Web server.

Running the Web server example on target hardware

The embOS/IP Web server sample application should always be the first step to check the proper function of the Web server with your target hardware.

Add all source files located in the following directories (and their subdirectories) to your project and update the include path:

  • Application\
  • Config\
  • Inc\
  • IP\
  • IP\IP_FS\FS_RO\
  • IP\IP_FS\FS_RO\Generated\

It is recommended that you keep the provided folder structure.

The sample application can be used on the most targets without the need for changing any of the configuration flags. The server processes up to 22 connections using the default configuration.

Note: 22 connections means that the target can handle up to 22 clients in parallel, if every client uses only one connection. Because a single Web browser often attempts to open more then one connection to a Web server to request the files (.gif, .jpeg, etc.) which are included in the requested Web page, the number of possible parallel connected clients is less than the number of possible connections.

The 22 connections split into 20 connections that are available to be kept in the backlog of a socket (which means that up to 20 connections wait to be fetched by the application with an accept()) and up to 2 connections currently processed.

Every connection is handled in an separate task. Therefore, the Web server uses up to three tasks in the default configuration, one task which listens on port 80 and accepts connections and two tasks to process the accepted connections. To modify the number of connections, only the macro MAX_CONNECTIONS has to be modified.

The most of the supplied sample Web pages include dynamic content, refer to Dynamic content for detailed information about the implementation of dynamic content.

Changing the file system type

By default, the Web server uses the supplied read-only file system. If a real file system like emFile should be used to store the Web pages, you have to modify the function _WebServerParentTask() of the example OS_IP_Webserver.c. Excerpt from OS_IP_Webserver.c:

/*********************************************************************
*
*        _WebServerParentTask
*
*/
static void _WebServerParentTask(void) {
  struct sockaddr Addr;
  struct sockaddr_in InAddr;
  U32 Timeout;
  long hSockListen;
  long hSock;
  int AddrLen;
  int i;
  int t;
  int t0;
  int r;
  WEBS_BUFFER_SIZES BufferSizes;

  Timeout = IDLE_TIMEOUT;
  IP_WEBS_SetFileInfoCallback(&_pfGetFileInfo);
  //
  // Assign file system
  //
  _pFS_API = &IP_FS_ReadOnly; // To use a a real filesystem like emFile
                              // replace this line.
  // _pFS_API = &IP_FS_FS;    // Use emFile

The usage of the read-only file system is configured with the following line:

_pFS_API = &IP_FS_ReadOnly;

To use emFile as file system for your Web server application, add the emFile abstraction layer IP_FS_FS.c to your project and change the line to:

_pFS_API = &IP_FS_FS;

Details about the "File system abstraction layer" and emFile can be found in the embOS/IP user manual.

Dynamic content

embos/IP supports different approaches to implement dynamic content in your Web server application. A Common Gateway Interface (CGI) like interface for static HTML pages with dynamic elements and virtual files which are completely generated from
the application.

Common Gateway Interface (CGI)

A Common Gateway Interface (CGI) like interface is used to implement dynamic content in Web pages. Every Web page will be parsed by the server each time a request is received. The server searches the Web page for a special tag. In the default configuration, the searched tag starts <!--#exec cgi=" and ends with "-->. The tag will be analyzed and the parameter will be extracted. This parameter specifies a serverside command and will be given to the user application, which can handle the command. The following screenshot shows the example page index.htm.

The HTML source for the page includes the following line:

<!--#exec cgi="Counter"-->

When the Web page is requested, the server parses the tag and the parameter Counter is searched for in an array of structures of type WEBS_CGI. The structure includes a string to identify the command and a pointer to the function which should be called if the parameter is found.

typedef struct {
  const char * sName; // e.g. "Counter"
  void (*pf)(WEBS_OUTPUT * pOutput, const char * sParameters, const char * sValue);
} WEBS_CGI;

In the example, Counter is a valid parameter and the function _callback_ExecCounter will be called. You need to implement the WEBS_CGI array and the callback functions in your application.

static const WEBS_CGI _aCGI[] = {
  {"Counter"   , _callback_ExecCounter },
  {"GetOSInfo" , _callback_ExecGetOSInfo},
  {"GetIPAddr" , _callback_ExecGetIPAddr},
  {NULL}
};

_callback_ExecCounter() is a simple example of how to use the CGI feature. It returns a string that includes the value of a variable which is incremented with every call to _callback_ExecCounter().

static void _callback_ExecCounter(WEBS_OUTPUT* pOutput,
                                  const char*  sParameters,
                                  const char*  sValue) {
  char ac[80];

  WEBS_USE_PARA(sParameters);
  WEBS_USE_PARA(sValue);
  _Cnt++;
  SEGGER_snprintf(ac, sizeof(ac), "<br><span class=\"hint\">Current page hit count: %lu</span></ul>", _Cnt);
  IP_WEBS_SendString(pOutput, ac);
}

If the Web page includes the CGI tag followed by an unknown command (for example, a typo like COounter instead of Counter in the source code of the Web page) an error message will be sent to the client.

Add new CGI functions to your Web server application

To define new CGI functions, three things have to be done.

1. Add a new command name which should be used as tag to the WEBS_CGI structure. For example: UserCGI

static const WEBS_CGI _aCGI[] = {
  {"Counter",  _callback_ExecCounter    },
  {"GetIndex", _callback_ExecGetIndex   },
  {"UserCGI",  _callback_ExecUserCGI    },
  {NULL,       _callback_DefaultHandler }
};

2. Implement the new function in your application source code.

static void _callback_ExecUserCGI(      WEBS_OUTPUT* pOutput,
                                  const char*        sParameters
                                  const char*        sValue ) {
  /* Add application code here */
}

3. Add the new tag to the source code of your Web page:

<!--#exec cgi="UserCGI"-->

Virtual files

embOS/IP supports virtual files. A virtual file is not a real file which is stored in the used file system. It is a function which is called instead. The function generates the content of a file and sends it to the client.

The Web server checks the extension of all requested files, the extension .cgi is by default used for virtual files. To change the extension that is used to detect a virtual file, refer to IP_WEBS_SetFileInfoCallback() in the embOS/IP user manual for detailed information.

The embOS/IP Web server comes with an example (CallVirtualFile.htm) that requests a virtual file. The sample Web page contains a form with two input test fields, named FirstName and LastName, and a button to transmit the data to the server.

When the button on the Web page is pressed, the file Send.cgi is requested. The embOS/IP Web server recognizes the extension .cgi , checks if a virtual file with the name Send.cgi is defined and calls the defined function. The function in the example is _callback_SendCGI() and gets the string FirstName=Foo&LastName=Bar as parameter.

typedef struct {
  const char * sName;
  void (*pf)(WEBS_OUTPUT * pOutput, const char * sParameters);
} WEBS_VFILES;

In the example, Send.cgi is a valid URI and the function _callback_SendCGI() will be called.

static const WEBS_VFILES _aVFiles[] = {
  {"Send.cgi", _callback_SendCGI },
  NULL
};

The virtual file Send.cgi gets two parameters. The strings entered in the input fields Firstname and LastName are transmitted with the URI. For example, you enter Foo in the first name field and Bar for last name and push the button. The browser will transmit the following string to our Web server:

Send.cgi?FirstName=Foo&LastName=Bar

You can parse the string and use it in the way you want to. In the example we parse the string and output the values on a Web page which is build from the function _callback_CGI_Send().

/*********************************************************************
*
*        _SendPageHeader
*
* Function description:
*   Sends the header of the virtual file.
*   The virtual files in our sample application use the same HTML layout.
*   The only difference between the virtual files is the content and that
*   each of them use an own title/heading.
*/
static void _SendPageHeader(WEBS_OUTPUT * pOutput, const char * sName) {
  IP_WEBS_SendString(pOutput, "<!DOCTYPE html><html><head><title>");
  IP_WEBS_SendString(pOutput, sName);
  IP_WEBS_SendString(pOutput, "</title>");
  IP_WEBS_SendString(pOutput, "<link href=\"Styles.css\" rel=\"stylesheet\"></head><body><header>");
  IP_WEBS_SendString(pOutput, sName);
  IP_WEBS_SendString(pOutput, "</header>");
  IP_WEBS_SendString(pOutput, "<div class=\"content\">");
}

/*********************************************************************
*
*        _SendPageFooter
*
* Function description:
*   Sends the footer of the virtual file.
*   The virtual files in our sample application use the same HTML layout.
*/
static void _SendPageFooter(WEBS_OUTPUT * pOutput) {
  IP_WEBS_SendString(pOutput, "<br><br><br>");
  IP_WEBS_SendString(pOutput, "</div><img src=\"Logo.gif\" alt=\"Segger logo\" class=\"logo\">");
  IP_WEBS_SendString(pOutput, "<footer><p><a href=\"index.htm\">Back to main</a></p>");
  IP_WEBS_SendString(pOutput, "<p>SEGGER Microcontroller GmbH &amp; Co. KG || <a href=\"http://www.segger.com\">www.segger.com</a> ");
  IP_WEBS_SendString(pOutput, "<span class=\"hint\">(external link)</span></p></footer></body></html>");
}

/*********************************************************************
*
*        _callback_CGI_Send
*/
static void _callback_CGI_Send(WEBS_OUTPUT * pOutput, const char * sParameters) {
  int r;
  const char * pFirstName;
  int FirstNameLen;
  const char * pLastName;
  int LastNameLen;

  //
  // Header of the page
  //
  _SendPageHeader(pOutput, "Virtual file sample");
  //
  // Content
  //
  r  = IP_WEBS_GetParaValuePtr(sParameters, 0, NULL, 0, &pFirstName, &FirstNameLen);
  r |= IP_WEBS_GetParaValuePtr(sParameters, 1, NULL, 0, &pLastName,  &LastNameLen);
  if (r == 0) {
    IP_WEBS_SendString(pOutput, "<h3>First name: ");
    IP_WEBS_SendMem(pOutput, pFirstName, FirstNameLen);
    IP_WEBS_SendString(pOutput, "</h3>");
    IP_WEBS_SendString(pOutput, "<h3>Last name: ");
    IP_WEBS_SendMem(pOutput, pLastName, LastNameLen);
    IP_WEBS_SendString(pOutput, "</h3>");
  } else {
    IP_WEBS_SendString(pOutput, "<br>Error!");
  }
  //
  // Footer of the page
  //
  _SendPageFooter(pOutput);
}

The output of _callback_CGI_Send() should be similar to:

AJAX

The embOS/IP Web server supports AJAX. AJAX is an acronym for Asynchronous JavaScript and XML. It is the foundation to build responsive and dynamic Web applications, which look and feel like desktop applications. AJAX is a special way to communicate with a Web server. In opposite to the old fashioned synchronous way where every data transmission to or from the server needs a reload of the whole Web page, AJAX works behind the scenes. With AJAX it is possible to grab the data you want and display it instantly in a Web page. No page refreshes needed, no waiting, no flickering in the browser. AJAX works on all major browsers.

AJAX combines some well-known Web techniques like HTML and CSS, JavaScript and XML. From the perspective of a developer the really interesting part AJAX are the XMLHttpRequests. An XMLHttpRequest object is an API available to Web browser scripting languages such as JavaScript and the core component for the asynchronous communication. The name XMLHttpRequest is a little bit misleading, since XMLHttpRequest can be used to send and receive any kind of data, not just XML. In most cases the exchanged data is plain text, HTML or JSON.

To exchange data without a reload of the whole page, you need create an XMLHttpRequest object. The way to create an XMLHttpRequest object is browser dependent. Therefore, it make sense to capsule the creation in a JavaScript function, which tries to handle every browser. An example is listed below:

  //
  // Create a XMLHttpRequest.
  // Tries to handle the different browser implementation
  //
  function _CreateRequest() {
    try {
      request = new XMLHttpRequest();
    } catch (tryMS) {
      try {
        request = new ActiveXObject("Msxml2.XMLHTTP");
      } catch (otherMS) {
        try {
          request = new ActiveXObject("Microsoft.XMLHTTP");
        } catch (failed) {
          request = null;
        }
      }
    }
  return request;
}

With an XMLHttpRequest object it is easy to exchange data with a Web server. The XMLHttpRequest object only needs an URI which should be requested from the server and a callback function, which will be called as soon as data will be received.

  //
  // Request the details from the server.
  //
  function _GetData(itemName) {
    request = _CreateRequest(); // Create an XMLHttpRequest
    if (request == null) {
      alert("Unable to create request");
      return;
    }
    var url= "../GetData.cgi";
    request.open("GET", url, true);
    request.onreadystatechange = _YourFunction;
    request.send(null);
  }

In the example GetData.cgi is a virtual file, which will be requested from the embOS/IP Web server and _YourFunction() is registered as callback function. For further information about the implementation of a virtual file, refer to Virtual files.

After setting the callback function, the request has to be sent. _YourFunction() will be called each time the readyState of the request object changes. To guarantee that the request is complete and the transmission status is ok, the readyState and HTTP status should be checked in your callback function.

  //
  // Request the details from the server.
  //
  function _DisplayDetails() {
    if (request.readyState == 4) { // Is the request complete ?
      if (request.status == 200) { // Status OK ?
        // Do something with the received data.
      }
    }
  }

The embOS/IP Web server comes with some sample Web pages to demonstrate how AJAX can be used to get and visualize data from your target.

File Description
Products.htm Simple AJAX sample. The Web page shows pictures of SEGGER middleware products. On click on one of the pictures an XMLHTTPRequest is created and additional product related information is requested from the embOS/IP Web server.
Shares.htm The sample demonstrates the usage of Server-Sent Events (SSE) and AJAX with the embOS/IP Web server. The displayed stock quotes table is random data generated by the server and updated every second via SSE. The graph is updated via AJAX. Every update of the stock prices table triggers the graph library to request the last 30 stock prices of the selected company to redraw the graph. All stock quotes are fictional. The goal of this example is to demonstrate how simple it is to visualize any kind of data with the embOS/IP Web server. The sample uses the open source JavaScript library RGraph for the visualization of the stock quotes. RGraph is an HTML5 charts library, which uses the MIT license. The latest version of the library can be downloaded from http://www.rgraph.net/.

All samples are hardware independent and tested with popular Web browsers like Internet Explorer, Firefox and Chrome.

For further information about AJAX, XMLHttpRequest handling, and data visualization we recommend one of the many available reference books.

Server-Sent Events (SSE)

Server-Sent Events (SSE) are an HTML5 technology which enables a Web server to push data to Web pages over HTTP. The Web browser establishes an initial connection, which is used for the Web server updates.

The embOS/IP Web server supports SSE to supply the Web browser with dynamic content. A Web browser can request information via EventSource objects. The idea behind SSE is that the Web server keeps an connection to Web browser open and sends data via this connection whenever it is necessary. This means that the Web browser receives data as a stream without polling. This reduces the HTTP protocol overhead.

To subscribe to an event stream, you have create an EventSource object and pass it the URL of your stream.

<script>
  if(typeof(EventSource) !== "undefined") {
    var source = new EventSource("SSETime.cgi");
    source.onmessage = function(event) {
      document.getElementById("Time").innerHTML = "<h2>" + event.data + "</h2>";
      document.getElementById("Time").innerHTML +=
      "The browser gets the system time via a Server-Sent Event (SSE).
      <br>No meta refresh (reload) required!";
    };
  } else {
    document.getElementById("Time").innerHTML =
    "Sorry, your browser does not support Server-Sent Events (SSE)...";
  }
</script>

The source code excerpt above creates a new EventSource object. The EventSource objects starts immediately listening for events on the given URL SSETime.cgi. Everytime when the Web browser will receive data, the data will be displayed on the Web page.

SSETime.cgi is implemented in the supplied embOS/IP Web Server sample application (Webserver_DynContent.c). From the perspective of the Web server it is a virtual file. Please refer to Virtual files for further information about virtual files. The following excerpt of Webserver_DynContent.c shows the implementation of the virtual file SSETime.cgi.

/*********************************************************************
*
*        _callback_CGI_SSETime
*
* Function description:
*   Sends the system time to the Web browser every 500 ms.
*/
static void _callback_CGI_SSETime(WEBS_OUTPUT * pOutput, const char * sParameters) {
  int r;

  IP_USE_PARA(sParameters);
  //
  // Construct the SSE Header
  //
  IP_WEBS_SendHeaderEx(pOutput, NULL, "text/event-stream", 1);
  IP_WEBS_SendString(pOutput, "retry: 1000\n");
  while(1) {
    r = _SendTime((void*)pOutput);
    if (r == 0) { // Data transmitted
      OS_Delay(500);
    } else {
      break;
    }
  }
}

First step to send data as an event stream is to send the MIME type text/event-stream to the Web browser. The Web browser attempts to reconnect to the Web server ~3 seconds after a connection is closed. You can change that timeout by sending a line beginning with retry:, followed by the number of milliseconds to wait before trying to reconnect. The event stream message is build in _SendTime(). After sending the data OS_Delay() suspends the task for 500ms. _SendTime() will be called again after the delay as long as the connection is open.

/*********************************************************************
*
*        _SendTime
*
* Function description:
*   Sends the system time to the Web browser.
*
* Return value:
*   0: Data successfully sent.
*  -1: Data not sent.
*   1: Data successfully sent. Connection should be closed.
*/
static int _SendTime(WEBS_OUTPUT * pOutput) {
  int r;
 
  //
  // Send implementation specific header to client
  //
  IP_WEBS_SendString(pOutput, "data: ");
  IP_WEBS_SendString(pOutput, "System time: ");
  IP_WEBS_SendUnsigned(pOutput, OS_GetTime32(), 10, 0);
  IP_WEBS_SendString(pOutput, "<br>");
  IP_WEBS_SendString(pOutput, "\n\n"); // End of the SSE data
  r = IP_WEBS_Flush(pOutput);
  return r;
}

The return value of _SendTime() is checked in _callback_CGI_SSETime(). This is necessary to prove if the data connection is still open. If the connection has been closed by the client, the endless loop will be left and the Web server will end the Web server child task.

SSE is currently not support by all popular browsers. The following Web browsers support Server-Sent Events natively.

Browser Supported Notes
Google Chrome Yes Starting with Chrome Ver. 27
MS Internet Explorer No --
Mozilla Firefox Yes Starting with Firefox Ver. 30
Opera Yes Starting with Ver. 23
Safari Yes Starting with Ver. 5.1

Since the Microsoft Internet Explorer does not support Server-Sent Events, we use the JavaScript library eventsource.js in our samples to make them also usable with Microsoft Internet Explorer. eventsource.j can be downloaded with the following link:

https://github.com/Yaffle/EventSource/

eventsource.js uses the MIT license. It can be used and modified according to your needs.

The embOS/IP Web server comes with some sample Web pages to demonstrate how SSE can be used to get and visualize data from your target.

File Description
Shares.htm The sample demonstrates the usage of Server-Sent Events (SSE) and AJAX with the embOS/IP Web server. For detailed information about the sample refer to AJAX.
SSE_IP.htm Shows some embOS/IP status information.
SSE_OS.htm Shows some embOS status information.
SSE_Time.htm Shows the system time.

Authentication

HTTP/1.0, includes the specification for a Basic Access Authentication scheme. The basic authentication scheme is a non-secure method of filtering unauthorized access to resources on an HTTP server, because the user name and password are passed over the network as clear text. It is based on the assumption that the connection between the client and the server can be regarded as a trusted carrier. As this is not generally true on an open network, the basic authentication scheme should be used accordingly.

The basic access authentication scheme is described in:

RFC# Description
[RFC 2617] HTTP Authentication: Basic and Digest Access Authentication
Direct download: ftp://ftp.rfc-editor.org/in-notes/rfc2617.txt

The "basic" authentication scheme is based on the model that the client must authenticate itself with a user-ID and a password for each realm. The realm value should be considered an opaque string which can only be compared for equality with other realms on that server. The server will service the request only if it can validate the user-ID and password for the protection space of the Request-URI. There are no optional authentication parameters.

Upon receipt of an unauthorized request for a URI within the protection space, the server should respond with a challenge like the following:

WWW-Authenticate: Basic realm="Embedded Web server"

where "embOS/IP embedded Web server" is the string assigned by the server to identify the protection space of the Request-URI. To receive authorization, the client sends the user-ID and password, separated by a single colon (":") character, within a base64 encoded string in the credentials.

If the user agent wishes to send the user-ID "user" and password "pass", it would use the following header field:

Authorization: Basic dXNlcjpwYXNz

Authentication example

The client requests a resource for which authentication is required:

GET /conf/Authen.htm HTTP/1.1
Host: 192.168.11.37

The server answers the request with a "401 Unauthorized" status page. The header of the 401 error page includes an additional line WWW-Authenticate. It includes the realm for which the proper user name and password should be transmitted from the client (for example, a Web browser).

HTTP/1.1 401 Unauthorized
Date: Mon, 04 Feb 2008 17:00:44 GMT
Server: embOS/IP
Accept-Ranges: bytes
Content-Length: 695
Connection: close
Content-Type: text/html
X-Pad: avoid browser bug
WWW-Authenticate: Basic realm="embOS/IP embedded Web server"

<HTML>
<HEAD><TITLE>401 Unauthorized</TITLE></HEAD>
<BODY>
<H1>401 Unauthorized</H1>
Browser not authentication-capable or authentication failed.<P>
</BODY>
</HTML>

The client interprets the header and opens a dialog box to enter the user name and password combination for the realm of the resource.

Note: The embOS/IP Web server example always uses the following user name and the password combination: User Name: user - Password: pass

Enter the proper user name/password combination for the requested realm and confirm with the OK button. The client encodes the user name/password combination to a base64 encoded string and requests the resource again. The request header is enhanced by the following line: Authorization: Basic dXNlcjpwYXNz

GET /conf/Authen.htm HTTP/1.1
Host: 192.168.11.37
Authorization: Basic dXNlcjpwYXNz

The server decodes the user name/password combination and checks if the decoded string matches to the defined user name/password combination of the realm. If the strings are identical, the server delivers the page. If the strings are not identical, the server answers again with a "401 Unauthorized" status page.

HTTP/1.1 200 OK
Content-Type: text/html
Server: embOS/IP
Expires: THU, 26 OCT 1995 00:00:00 GMT
Transfer-Encoding: chunked
200
<HTML>
 <HEAD>
 <TITLE>Web server configuration</TITLE>
 </HEAD>
 <BODY>
 <!-- Content of the page -->
 </BODY>
</HTML>
0

Configuration of the authentication

The embOS/IP Web server checks the access rights of every resource before returning it. The user can define different realms to separate different parts of the Web server resources. An array of WEBS_ACCESS_CONTROL structures has to be implemented in the user application. Refer to structure WEBS_ACCESS_CONTROL in the embOS/IP user manual for detailed information. If no authentication should be used, the array includes only one entry for the root path.

WEBS_ACCESS_CONTROL _aAccessControl[] = {
  { "/", NULL, NULL },
  0
};

To define a realm "conf", an additional WEBS_ACCESS CONTROL entry has to be implemented.

WEBS_ACCESS_CONTROL _aAccessControl[] = {
  { "/conf/", "Login for configuration", "user:pass" },
  { "/", NULL, NULL },
  0
};

The string "Login for configuration" defines the realm. "user:pass" is the user name/password combination stored in one string.

Form handling

The embOS/IP Web server supports both POST and GET actions to receive form data from a client. POST submits data to be processed to the identified resource. The data is included in the body of the request. GET is normally only used to requests a resource, but it is also possible to use GET for actions in Web applications. Data processing on server side might create a new resource or update existing resources or both.

Every HTML form consists of input items like textfields, buttons, checkboxes, etc. Each of these input items has a name tag. When the user places data in these items in the form, that information is encoded into the form data. Form data is a stream of <name>=<value> pairs separated by the " & " character. The value each of the input item is given by the user is called the value. The <name>=<value> pairs are URL encoded, which means that spaces are changed into " + " and special characters are encoded into hexadecimal values. Refer to [RFC 1738] for detailed information about URL encoding. The parsing and decoding of form data is handled by the embOS/IP Web server. Thereafter, the server calls a callback function with the decoded and parsed strings as parameters. The responsibility to implement the callback function is on the user side.

Valid characters for CGI function names:

  • A-Z
  • a-z
  • 0-9
  • . _ -

Valid characters for CGI parameter values:

  •  A-Z
  • a-z
  • 0-9
  • All URL encoded characters
  • . _ - *()!$\

Simple form processing sample

The following example shows the handling of the output of HTML forms with your Web server application. The example Web page FormGET.htm implements a form with three inputs, two text fields and one button.

An excerpt of the HTML code of the Web page as it is added to the server is listed below:

<hr>
  <h2>Please enter your name...</h2>
  <h3>Hello <!--#exec cgi="FirstName"--> <!--#exec cgi="LastName"-->!</h3>
  <form action="" method="GET">
    <label for="FirstName">First name: </label>
    <input name="FirstName" type="text" size="30" maxlength="30" value="<!--#exec cgi="FirstName"-->">&nbsp;
    <label for="LastName">Last name: </label>
    <input name="LastName" type="text" size="30" maxlength="30" value="<!--#exec cgi="LastName"-->">&nbsp;
    <input type="submit" value="Change">
  </form>
<hr>

The action field of the form can specify a resource that the browser should reference when it sends back filled-in form data. If the action field defines no resource, the current resource will be requested again.

If you request the Web page from the embOS/IP Web server and check the source of the page in your Web browser, the CGI parts "<!--#exec cgi="FirstName"-->" and "<!--#exec cgi="LastName"-->" will be executed before the page will be transmitted to the server, so that in the example the values of the value= fields will be empty strings.

The HTML code of the Web page as seen by the Web browser is listed below:

<!DOCTYPE html>
<html>
  <head>
    <title>Virtual file sample</title>
    <link href="Styles.css" rel="stylesheet">
  </head>
  <body>
    <header>Virtual file sample</header>
    <div class="content">
      <form action="Send.cgi" method="GET">
        <label for="FirstName">First name: </label>
        <input name="FirstName" type="text" size="30" maxlength="30" value="">&nbsp;
        <label for="LastName">Last name: </label>
        <input name="LastName" type="text" size="30" maxlength="30" value="">&nbsp;
        <input type="submit" value="Change">
      </form>
    </div>
    <img src="Logo.gif" alt="Segger company logo" class="logo">
    <footer>
      <p>
        <a href="index.htm">
        Back to main</a></p><p>SEGGER Microcontroller GmbH &amp; Co. KG ||
        <a href="http://www.segger.com">www.segger.com</a>
        <span class="hint">(external link)</span>
      </p>
    </footer>
  </body>
</html>

To start form processing, you have to fill in the FirstName and the LastName field and click the Send button. In the example, the browser sends a GET request for the resource referenced in the form and appends the form data to the resource name as an URL encoded string. The form data is separated from the resource name by "?". Every <name>=<value> pair is separated by "&".

For example, if you type in the FirstName field John and Doe in the LastName field and confirm the input by clicking the Send button, the following string will be transmitted to the server and shown in the address bar of the browser.

http://192.168.11.37/FormGET.htm?FirstName=John&LastName=Doe

Note: If you use POST as HTTP method, the name>=<value> pairs are not shown in the address bar of the browser. The <name>=<value> pairs are in this case included in the entity body.

The embOS/IP Web server parses the form data. The <name> field specifies the name of a CGI function which should be called to process the <value> field. The server checks therefore if an entry is available in the WEBS_CGI array.

static const WEBS_CGI _aCGI[] = {
  {"FirstName", _callback_ExecFirstName},
  {"LastName",  _callback_ExecLastName },
  {NULL}
};

If an entry can be found, the specified callback function will be called.

The callback function for the parameter FirstName is defined as follow:

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/
static char _acFirstName[12];

/*********************************************************************
*
*       _callback_FirstName
*/
static void _callback_ExecFirstName(       WEBS_OUTPUT* pOutput,
                                     const char*        sParameters,
                                     const char*        sValue ) {
 if (sValue == NULL) {
   IP_WEBS_SendString(pOutput, _acFirstName);
 } else {
   _CopyString(_acFirstName, sValue, sizeof(_acFirstName));
 }
}

The function returns a string if sValue is NULL. If sValue is defined, it will be written into a character array. Because HTTP transmission methods GET and POST only transmit the value of filled input fields, the same function can be used to output a stored value of an input field or to set a new value. The example Web page shows after entering and transmitting the input the string Hello John Doe above the input fields until you enter and transmit another name to the server.

File upload

The embOS/IP Web server supports file uploads from the client. For this to be possible a real file system has to be used and the define WEBS_SUPPORT_UPLOAD has to be defined to "1".

From the application side uploading a file in general is the same as for other form data as described in Form handling. For file uploading a <form> field with encoding of type multipart/form-data is needed. An upload form field may contain additional input fields that will be parsed just as if using a non upload formular and can be parsed in your callback using either IP_WEBS_GetParaValue() or IP_WEBS_GetParaValuePtr().

Simple form upload sample

The following example shows the handling of file uploads with your Web server application. The example Web page Upload.htm implements a form with a file upload field.

The HTML code of the Web page as it is added to the server is listed below:

<HTML>
  <BODY>
    <CENTER>
      <P>
        <form action="Upload.cgi" method="post" enctype="multipart/form-data">
          <p>Select a file: <input name="Data" type="file">
          </p>
          <input type="submit"><input type="reset">
        </form>
      </P>
    </CENTER>
  </BODY>
</HTML>

The action field of the form can specify a resource that the browser should reference when it has finished handling the file upload. If the action field defines no resource, the current resource will be requested again.

To upload a file, you have to select a file by using the browse button and select a file to upload and click the Send button. In the example, the browser sends a POST request for the resource referenced in the form and appends the form and file data in an encoded string.

The embOS/IP Web server parses additional form data passed besides the file to be uploaded. This works the same as handling form data described in Form handling. The action parameter of the <form> field specifies the name of a virtual file that should be processed. A callback can then be used to provide an answer page referring the state of the upload. The example below shows how to check the success of an upload using a virtual file provided by the WEBS_VFILES array:

static const WEBS_VFILES _aVFiles[] = {
  {"Upload.cgi", _callback_CGI_UploadFile },
  { NULL, NULL }
};

If an entry can be found, the specified callback function will be called.

The callback function for the file Upload.cgi is defined as follow:

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/
/*********************************************************************
*
*       _callback_CGI_UploadFile
*/
static void _callback_CGI_UploadFile(WEBS_OUTPUT* pOutput, const char* sParameters) {
        int r;
  const char * pFileName;
        int FileNameLen;
  const char * pState; // Can be 0: Upload failed;
                       // 1: Upload succeeded; Therefore we do not need to
                       // know the length, it will always be 1.

  IP_WEBS_SendString(pOutput, "<HTML><BODY>");
  r = IP_WEBS_GetParaValuePtr(sParameters, 0, NULL, 0, &pFileName, &FileNameLen);
  r |= IP_WEBS_GetParaValuePtr(sParameters, 1, NULL, 0, &pState , NULL);
  if (r == 0) {
    IP_WEBS_SendString(pOutput, "Upload of \"");
    IP_WEBS_SendMem(pOutput, pFileName, FileNameLen);
    if (*pState == '1') {
      IP_WEBS_SendString(pOutput, "\" successful!<br>");
      IP_WEBS_SendString(pOutput, "<a href=\"");
      IP_WEBS_SendMem(pOutput, pFileName, FileNameLen);
      IP_WEBS_SendString(pOutput, "\">Go to ");
      IP_WEBS_SendMem(pOutput, pFileName, FileNameLen);
      IP_WEBS_SendString(pOutput, "</a><br>");
    } else {
      IP_WEBS_SendString(pOutput, "\" not successful!<br>");
    }
  } else {
    IP_WEBS_SendString(pOutput, "Upload not successful!");
  }
  IP_WEBS_SendString(pOutput, "</BODY></HTML>");
}

In addition to the provided form fields from the upload form used two additional entries will be added to the end of the parameter list available for parsing:

  • The original filename of the file uploaded
  • The status of the upload process. This can be 0: Upload failed or 1: Upload succeeded.

The example Web page shows after the upload has been finished.

The source of the Web page as seen by the Web browser is listed below:

<HTML><BODY>
Upload of "1.gif" successful!<br>
<a href="1.gif">Go to 1.gif</a><br>
</BODY></HTML>

Configuration

The embOS/IP Web server can be used without changing any of the compile time flags. All compile time configuration flags are preconfigured with valid values, which match the requirements of most applications.

Compile time configuration

The embOS/IP Web server can be used without changing any of the compile time flags. All compile time configuration flags are preconfigured with valid values, which match the requirements of most applications.

The following types of configuration macros exist:

Binary switches "B"

Switches can have a value of either 0 or 1, for deactivated and activated respectively. Actually, anything other than 0 works, but 1 makes it easier to read a configuration file. These switches can enable or disable a certain functionality or behavior. Switches are the simplest form of configuration macros.

Numerical values "N"

Numerical values are used somewhere in the source code in place of a numerical constant. A typical example is the configuration of the sector size of a storage medium.

Alias "A"

A macro which operates like a simple text substitute. An example would be the define U8, which the preprocessor would replace with unsigned char.

Function replacements "F"

Macros can basically be treated like regular functions although certain limitations apply, as a macro is still put into the source code as simple text replacement. Function replacements are mainly used to add specific functionality to a module which is highly hardware-dependent. This type of macro is always declared using brackets (and optional parameters).

Compile time configuration switches

Type Symbolic name Default Description
F WEBS_WARN -- Defines a function to output warnings. In debug configurations (DEBUG
== 1
) WEBS_WARN maps to IP_Warnf_Application().
F WEBS_LOG -- Defines a function to output logging messages. In debug configurations (DEBUG == 1) WEBS_LOG maps to IP_Logf_Application().
N WEBS_IN_BUFFER_SIZE 512 Defines the size of the input buffer. The input buffer is used to store the HTTP client requests. Please refer to Runtime configuration for further information about the usage.
N WEBS_OUT_BUFFER_SIZE 512 Defines the size of the output buffer. The output buffer is used to store the HTTP response. Please refer to Runtime configuration for further information about the usage.
N WEBS_PARA_BUFFER_SIZE 256 Defines the size of the buffer used to store the parameter/value string that is given to a virtual file. If virtual files are not used in your application, remove the definition from WEBS_Conf.h to save RAM. Please refer to Runtime configuration for further information about the usage.
N WEBS_FILENAME_BUFFER_SIZE 64 Defines the size of the buffer used to store the requested URI/filename to access on the filesystem. Please refer to Runtime configuration for further information about the usage.
N WEBS_TEMP_BUFFER_SIZE 256 Defines the size of the TEMP buffer used internally by the Web server.
N WEBS_AUTH_BUFFER_SIZE 32 Defines the size of the buffer used to store the authentication string. Refer to Authentication for detailed information about authentication.
N WEBS_FILENAME_BUFFER_SIZE 32 Defines the size of the buffer used to store the filename strings.
B WEBS_SUPPORT_UPLOAD 0/1 Defines if file upload is enabled. Defaults to 0: Not enabled, for source code shipments and 1: Enabled, for object shipments. If you do not use the upload feature, define WEBS_SUPPORT_UPLOAD to 0.
N WEBS_URI_BUFFER_SIZE 0 Defines the size of the buffer used to store the full URI of the accessed resource. By default this feature is disabled.
N WEBS_MAX_ROOT_PATH_LEN 12 Maximum allowed root path length of the Web server in multiples of a CPU native unit (typically int). If the root path of the Web server is the root of your media you can comment out this define or set it to zero. Example:
For the root path "/httpdocs" the define needs to be set to at least 9. As this is not a multiple of an int, set it to 12.

Status message Web pages

The status message Web pages are visualizations of the information transmitted to the client in the header of the Web server response. Because these visualizations are not required for the functionality of the Web server, the macros can be empty.

Type Symbolic name Default
A WEBS_401_PAGE "<HTML>\n" \
"<HEAD>\n" \
"<TITLE>401 Unauthorized</TITLE>\n" \
"</HEAD>\n" \
"<BODY>\n" \
"<H1>401 Unauthorized</H1>\n" \
"Browser not authentication-capable" \
"or authentication failed.\n" \
"</BODY>\n" \
"</HTML>\n"
A WEBS_404_PAGE "<HTML>\n" \
"<HEAD>\n" \
"<TITLE>404 Not Found</TITLE>\n" \
"</HEAD>\n" \
"<BODY>\n" \
"<H1>404 Not Found</H1>\n" \
"The requested document was not " \
"found on this server.\n" \
"</BODY>\n" \
"</HTML>\n"
A WEBS_501_PAGE "<HTML>\n" \
"<HEAD>\n" \
"<TITLE>501 Not implemented</TITLE>\n" \
"</HEAD>\n" \
"<BODY>\n" \
"<H1>Command is not implemented</H1>\n" \
"</BODY>\n" \
"</HTML>\n"
A WEBS_503_PAGE "<HTML>\n" \
"<HEAD>\n" \
"<TITLE>503 Connection limit reached</TITLE>\n" \
"</HEAD>\n" \
"<BODY>\n" \
"<H1>503 Connection limit reached</H1>\n" \
"The max. number of simultaneous connections to " \
"this server reached.<P>\n" \
"Please try again later.\n" \
"</BODY>\n" \
"</HTML>\n"

Runtime configuration

The input buffer, the output buffer, the parameter buffer, the filename buffer and the maximum root path length are runtime configurable. Up to version 2.20h compile time switches WEBS_IN_BUFFER_SIZE, WEBS_OUT_BUFFER_SIZE and WEBS_PARA_BUFFER_SIZE were used to configure the sizes of the buffers. These compile time switches along with new switches like WEBS_MAX_ROOT_PATH_LEN are still available to guarantee compatibility to previous versions and are used as default values for the buffer sizes in applications where the runtime configuration function IP_WEBS_ConfigBufSizes() is not called. For further information, please refer to IP_WEBS_ConfigBufSizes() in the embOS/IP user manual.

 For more information download the windows trial version which includes the embOS/IP manual.