Tiny HTTP Server - thttpd
#include <sys.h>
#include <stdio.h>
#include <tcpinit.h>
#include <sys/socket.h>
#include <thttpd.h>
thttpd_main((char *)httpdconf_name);
thttpd is simple of use and fast solution for adding HTTP support to your application.
The thttpd server provides the capability to provide the following services and support
HTTP 0.9, 1.0 and 1.1 support
Support for GET, POST, HEAD
Support CGI interface for user applications
Minimal Footprint in both RAM and Flash
Simple C language callout to deal with forms, buttons and login parse by user
Optional ogin support
Chunked file transfers
Secure operation
Thread only implementation
Support for multiple file systems.
Error Responses:
HTTP/1.X 404 PAGE NOT FOUND
HTTP/1.X 500 SERVER ERROR
HTTP/1.X 400 BAD REQUEST
Thttpd server requires for work TCP and FatFs/Fsys servers. This servers must be started before run thttpd.
Thttpd starting by command:
thttpd_main((char *)httpdconf_name);
Where httpdconf_name is a full configuration file name. This file holds basic configuration parameters for thttp and has another structure:
Pram:value
Pram:value
...
Pram:value
|
Param |
Description |
Sample |
|
server_software |
software product name |
UnisonHttpd |
|
http_index |
default start page |
index.html |
|
http_root |
server root folder |
/dev/rd/thttp/ |
|
http_port |
default http port |
80 |
|
server_name |
server name, used by http protocol |
Thttpd server |
|
host_name |
host name, used by http protocol |
Myhost |
|
cgi_ena |
enable cgi handling (0 or 1) |
1 |
|
data_timeout |
watchdog to handle physical link loss |
20 |
|
keepalive_timeout |
timeout to keep socket opened, after handling browser requests |
30 |
Thttpd server can provide access to some pages by password.
To setup the login information, users need to do the following:
Put the pages in a sub directory of thttpd which has limited access
Create a password file and set it up for the thttpd server.
Initialize the login table.
Add any users that the user requires
Example:
#include <login.h> set_loginfile(htpasswd_name); //htpasswd_name - full path to .htpasswd file init_login_table(htpasswd_name);
To add or delete new user run:
add_user( "username", "passwd" ); delete_user( "username");
Now if client access to protected files he must enter valid password and username. Server can support many different users
If application need to have CGI getaway support you need:
set in “httpdconf” file option “cgi_ena” to 1
register cgi haldler to one or many server folder.
Example:
register_cgi_handler("name", "URL_path", cgi_handler_funk);
Parametrs:
name – handler name for server. Name could be any “string”. This name is used to register handler in server and deletes. Any existing handler with the same name. Handler names should NOT be duplicated in system!
URL_path – address. which should be specified in the input POST to get server automatically call user function for handling this request. Addresses (or path names) should not be duplicated.
cgi_handler_func – pointer to function-handler which will handle requests any existing handler with the same name.
It is possible to register a few cgi handlers in system. It is also possible to register one handler for different addresses.
Example:
register_cgi_handler ("test1", "cgi-bin/userI1.cgi", UI_cgi_handler);
register_cgi_handler ("test2", "cgi-bin/userI2.cgi", UI_cgi_handler);
register_cgi_handler ("test3", "cgi-bin/userI3.cgi", ABC_cgi_handler);
In this way the server will use one handler UI_cgi_handler for addresses
XXX/cgi-bin/userI1.cgi and XXX/cgi-bin/userI2.cgi. It will use a completely different handler ABC_cgi_handler for XXX/cgibin/userI3.cgi
To delete registerd cgi handler run del_cgi_handler
Example:
del_cgi_handler(“name”);
name – handler name for server.
To handle all user data which is input from the Browser a next parameters list is passed into the cgi-handler.
int cgi_handler (char * path , char ** argv , char ** envp, int fd);
*path – path for which this handler is called. For instance, "cgi-bin/userI.cgi"
**argv – pointer array to parameters strings (to date it is 2 max)
argv[0] – called file name. For instance "userI.cgi"
argv[1] – If parameters are passed using method GET, then in argv[1] string with this parameters is passed, else (null)
**envp – pointers array to strings, which is containing information, passed by Browser using http headers (according to CGI standard). This array can contain variable numbers of elements, with the array ended by '\0'.
Example:
HTTP_USER_AGENT=Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.2.15 Version/10.10
HTTP_HOST=192.168.16.201
HTTP_ACCEPT=text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/xxbitmap,*/*;q=0.1
HTTP_ACCEPT_LANGUAGE=ru-RU,ru;q=0.9,en;q=0.8
HTTP_ACCEPT_CHARSET=iso-8859-1, utf-8, utf-16, *;q=0.1
HTTP_ACCEPT_ENCODING=deflate, gzip, x-gzip, identity, *;q=0
HTTP_REFERER=http://192.168.16.201/
HTTP_CONNECTION=Keep-Alive, TE
HTTP_TE=deflate, gzip, chunked, identity, trailers
HTTP_CONTENT_LENGTH=43
HTTP_CONTENT_TYPE=application/x-www-form-urlencoded
SERVER_NAME=Myhost
GATEWAY_INTERFACE=CGI/1.1
SERVER_PROTOCOL=HTTP/1.1
REQUEST_METHOD=POST
SCRIPT_NAME=/cgi-bin/userI.cgi
SCRIPT_FILENAME=cgi-bin/userI.cgi
SERVER_SOFTWARE=UnisonHttpd
REQUEST_URI=cgi-bin/userI.cgi
CONTENT_TYPE=application/x-www-form-urlencoded
CONTENT_LENGTH=43
SERVER_PORT=1568
REMOTE_ADDR=192.168.18.1fd – socket ID, from which the handler will read data and where the handler will write the newly created page
In summary, the cgi_handler function inputs the parameters, processes the input arguments and creates a new page which is streamed back to the browser to update the user.
You can parse input parameters **envp by use function getenv(“header name”, envp); and getparam(str, "field=");
Example:
env = getenv("REQUEST_METHOD", envp);
if (strcmp(env, "POST")==0) { //metod == "POST"
size = atoi(getenv("CONTENT_LENGTH", envp)); //getting recived data size
pos = recv(fd, str, size, 0); //reciving data from stream
str[pos] = '\0';
}else { //metod == "GET"
env = getenv("QUERY_STRING", envp); //Reading query string sended by GET metod
strcpy(str,env);
}
name = getparam(str,"name_field="); //get entered "user Name"
age = getparam(str,"age_field="); //get entered "user Age"
Note: These CGI handlers must be compiled into the executable as part of the image. This means that changes to the web pages create a need to update the CGI handlers if the updates are more than cosmetic, or in other words, new variables are added.
pthread_t tid;
pthread_attr_t attr;
struct fsysinit fsysinit;
struct sched_param myNewPriority;
int mkfs_status, mount_status;
int fd;
xprintf("Start Main\n");
/*FSYS Initialization*/
fsysinit.fsi_msgsize = sizeof(struct fsysinit);
fsysinit.fsi_nclones = 1;
fsysinit.fsi_datalog = 0x3;
fsysinit.fsi_nbuffers = 4; /* num of disk buffers (cache size) */
fsysinit.fsi_maxopen = 2; /* max num of open files */
fsysinit.fsi_blocksize = 256;
// set up stack size and priority for main fsys threads and all clones
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, FSYSSTACKSIZE);
myNewPriority.sched_priority = FSYSPRIO;
pthread_attr_setschedparam(&attr, &myNewPriority);
if(!pthread_create(&tid, &attr,(void*(*)(void *))&_file_server, (char*)&fsysinit))
{
if(!dir_register(FSYS_DIRECTORY, tid, TYPE_SERVER))
{
xprintf("..Unable to register name of File Server\n..");
xprintf("..Required resource not present - aborting..\n");
pthread_exit((void*)1);
}
}
else
{
xprintf("..Unable to create File Server..\n");
xprintf("..Required resource for program not present - aborting..\n");
pthread_exit((void*)1);
}
xprintf("..File server is now created.\n..");
xprintf("\n..Attempting to mount it...\n");
if((mount_status = mount(DEVICE_NO, FSYS_MOUNT, 0)) == -1) {
xprintf("..Unable to mount flash disk. errno=%d\n", errno);
xprintf("..Please fix me\n..");
pthread_exit((void*)1);
}
xprintf("..done fsys initialization..\n");
set_loginfile(htpasswd_name); //set login and pass to access to files in this dir
init_login_table(htpasswd_name);
add_user( "root", "root" );
/* make virtual link to user handler call address
* http://xxx.xxx.xxx.xxx/cgi-bin/userI.cgi
* will run UI_cgi_handler();
*/
register_cgi_handler("test", "cgi-bin/userI.cgi", UI_cgi_handler);
/* Start tcpd server*/
xprintf("\n--- Start tcpd ---\n");
myNewPriority.sched_priority = TCPRIO;
pthread_attr_setschedparam(&attr, &myNewPriority);
pthread_attr_setstacksize(&attr, TCPSTACKSIZE);
if(pthread_create(&tid, &attr, &tcp_shell, 0)!=0)
{
xprintf("pthread_create = %d\n", errno);
pthread_exit(0);
}
if(dir_register("/dev/tcpd", tid, TYPE_SERVER)==0)
{
xprintf("dir_register = %d\n", errno);
pthread_exit(0);
}
/* Start THTTP server */
xprintf("\n--- Start HTTP server ---\n");
// set up stack size and priority for main THTTP thread
pthread_attr_setstacksize(&attr, THTTPSTACKSIZE);
myNewPriority.sched_priority = THTTPPRIO;
pthread_attr_setschedparam(&attr, &myNewPriority);
if(pthread_create(&tid, &attr,(void*(*)(void *))&thttp_shell, NULL)!=0)
{
xprintf("..Unable to create THTTP Server..\n");
pthread_exit((void*)1);
}
THREAD tcp_shell(void * args)
{
tcpd((char *)ðerinit, 0);
return (THREAD)0;
}
THREAD thttp_shell(void * args)
{
thttpd_main((char *)httpdconf_name);
return (THREAD)0;
}There is demo available for the Unison and DSPnano thttpd which are found in installdir/demos which provide examples of using the server with simple page, password protected and CGI generated pages.