Difference between revisions of "Httpd.c"
From Nutwiki
m |
m (1 revision imported) |
(No difference)
| |
Latest revision as of 16:02, 27 October 2016
Ein HTTP-Server. Falls das Netzwerk DHCP unterstützt, wird die IP-Adresse von dem DHCP-Server bezogen. Andernfalls wird die IP-Adresse 192.168.192.35 verwendet. Da ein HTTP-Server grundsätzlich Dateien ausliefert, wird ein Dateisystem im Flash-Speicher abgebildet.
/*
* Unique MAC address of the Ethernut Board.
*
* Ignored if EEPROM contains a valid configuration.
*/
#define MY_MAC "\x00\x06\x98\x30\x00\x35"
/*
* Unique IP address of the Ethernut Board.
*
* Ignored if DHCP is used.
*/
#define MY_IPADDR "192.168.192.35"
/*
* IP network mask of the Ethernut Board.
*
* Ignored if DHCP is used.
*/
#define MY_IPMASK "255.255.255.0"
/*
* Gateway IP address for the Ethernut Board.
*
* Ignored if DHCP is used.
*/
#define MY_IPGATE "192.168.192.1"
/* ICCAVR Demo is limited. Try to use the bare minimum. */
#if !defined(__IMAGECRAFT__)
/* Wether we should use DHCP. */
#define USE_DHCP
/* Wether we should run a discovery responder. */
#define USE_DISCOVERY
/* Wether to use PHAT file system. */
//#define USE_PHAT
#endif /* __IMAGECRAFT__ */
#ifdef USE_PHAT
#if defined(ETHERNUT3)
/* Ethernut 3 file system. */
#define MY_FSDEV devPhat0
#define MY_FSDEV_NAME "PHAT0"
/* Ethernut 3 block device interface. */
#define MY_BLKDEV devNplMmc0
#define MY_BLKDEV_NAME "MMC0"
#elif defined(AT91SAM7X_EK)
/* SAM7X-EK file system. */
#define MY_FSDEV devPhat0
#define MY_FSDEV_NAME "PHAT0"
/* SAM7X-EK block device interface. */
#define MY_BLKDEV devAt91SpiMmc0
#define MY_BLKDEV_NAME "MMC0"
#elif defined(AT91SAM9260_EK)
/* SAM9260-EK file system. */
#define MY_FSDEV devPhat0
#define MY_FSDEV_NAME "PHAT0"
/* SAM9260-EK block device interface. */
#define MY_BLKDEV devAt91Mci0
#define MY_BLKDEV_NAME "MCI0"
#endif
#endif /* USE_PHAT */
#ifndef MY_FSDEV
#define MY_FSDEV devUrom
#endif
#ifdef MY_FSDEV_NAME
#define MY_HTTPROOT MY_FSDEV_NAME ":/"
#endif
#include <cfg/os.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>
#include <dev/board.h>
#include <dev/urom.h>
#include <dev/nplmmc.h>
#include <dev/sbimmc.h>
#include <fs/phatfs.h>
#include <sys/version.h>
#include <sys/thread.h>
#include <sys/timer.h>
#include <sys/heap.h>
#include <sys/confnet.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/route.h>
#include <pro/httpd.h>
#include <pro/dhcp.h>
#include <pro/ssi.h>
#include <pro/asp.h>
#include <pro/discover.h>
#ifdef NUTDEBUG
#include <sys/osdebug.h>
#include <net/netdebug.h>
#endif
static char *html_mt = "text/html";
/**************************************************************/
/* ASPCallback */
/* */
/* This routine must have been registered by */
/* NutRegisterAspCallback() and is automatically called by */
/* NutHttpProcessFileRequest() when the server process a page */
/* with an asp function. */
/* */
/* Return 0 on success, -1 otherwise. */
/**************************************************************/
static int ASPCallback (char *pASPFunction, FILE *stream)
{
if (strcmp(pASPFunction, "usr_date") == 0) {
fprintf(stream, "Dummy example: 01.01.2005");
return(0);
}
if (strcmp(pASPFunction, "usr_time") == 0) {
fprintf(stream, "Dummy example: 12:15:02");
return(0);
}
return (-1);
}
/*
* CGI Sample: Show request parameters.
*
* See httpd.h for REQUEST structure.
*
* This routine must have been registered by NutRegisterCgi() and is
* automatically called by NutHttpProcessRequest() when the client
* request the URL 'cgi-bin/test.cgi'.
*/
static int ShowQuery(FILE * stream, REQUEST * req)
{
char *cp;
/*
* This may look a little bit weird if you are not used to C programming
* for flash microcontrollers. The special type 'prog_char' forces the
* string literals to be placed in flash ROM. This saves us a lot of
* precious RAM.
*/
static prog_char head[] = "<HTML><HEAD><TITLE>Parameters</TITLE></HEAD><BODY><H1>Parameters</H1>";
static prog_char foot[] = "</BODY></HTML>";
static prog_char req_fmt[] = "Method: %s<BR>\r\nVersion: HTTP/%d.%d<BR>\r\nContent length: %d<BR>\r\n";
static prog_char url_fmt[] = "URL: %s<BR>\r\n";
static prog_char query_fmt[] = "Argument: %s<BR>\r\n";
static prog_char type_fmt[] = "Content type: %s<BR>\r\n";
static prog_char cookie_fmt[] = "Cookie: %s<BR>\r\n";
static prog_char auth_fmt[] = "Auth info: %s<BR>\r\n";
static prog_char agent_fmt[] = "User agent: %s<BR>\r\n";
/* These useful API calls create a HTTP response for us. */
NutHttpSendHeaderTop(stream, req, 200, "Ok");
NutHttpSendHeaderBot(stream, html_mt, -1);
/* Send HTML header. */
fputs_P(head, stream);
/*
* Send request parameters.
*/
switch (req->req_method) {
case METHOD_GET:
cp = "GET";
break;
case METHOD_POST:
cp = "POST";
break;
case METHOD_HEAD:
cp = "HEAD";
break;
default:
cp = "UNKNOWN";
break;
}
fprintf_P(stream, req_fmt, cp, req->req_version / 10, req->req_version % 10, req->req_length);
if (req->req_url)
fprintf_P(stream, url_fmt, req->req_url);
if (req->req_query)
fprintf_P(stream, query_fmt, req->req_query);
if (req->req_type)
fprintf_P(stream, type_fmt, req->req_type);
if (req->req_cookie)
fprintf_P(stream, cookie_fmt, req->req_cookie);
if (req->req_auth)
fprintf_P(stream, auth_fmt, req->req_auth);
if (req->req_agent)
fprintf_P(stream, agent_fmt, req->req_agent);
/* Send HTML footer and flush output buffer. */
fputs_P(foot, stream);
fflush(stream);
return 0;
}
/*
* CGI Sample: Show list of threads.
*
* This routine must have been registered by NutRegisterCgi() and is
* automatically called by NutHttpProcessRequest() when the client
* request the URL 'cgi-bin/threads.cgi'.
*/
static int ShowThreads(FILE * stream, REQUEST * req)
{
static prog_char head[] = "<HTML><HEAD><TITLE>Threads</TITLE></HEAD><BODY><H1>Threads</H1>\r\n"
"<TABLE BORDER><TR><TH>Handle</TH><TH>Name</TH><TH>Priority</TH><TH>Status</TH><TH>Event<BR>Queue</TH><TH>Timer</TH><TH>Stack-<BR>pointer</TH><TH>Free<BR>Stack</TH></TR>\r\n";
#if defined(__AVR__)
static prog_char tfmt[] =
"<TR><TD>%04X</TD><TD>%s</TD><TD>%u</TD><TD>%s</TD><TD>%04X</TD><TD>%04X</TD><TD>%04X</TD><TD>%u</TD><TD>%s</TD></TR>\r\n";
#else
static prog_char tfmt[] =
"<TR><TD>%08lX</TD><TD>%s</TD><TD>%u</TD><TD>%s</TD><TD>%08lX</TD><TD>%08lX</TD><TD>%08lX</TD><TD>%lu</TD><TD>%s</TD></TR>\r\n";
#endif
static prog_char foot[] = "</TABLE></BODY></HTML>";
static char *thread_states[] = { "TRM", "<FONT COLOR=#CC0000>RUN</FONT>", "<FONT COLOR=#339966>RDY</FONT>", "SLP" };
NUTTHREADINFO *tdp = nutThreadList;
/* Send HTTP response. */
NutHttpSendHeaderTop(stream, req, 200, "Ok");
NutHttpSendHeaderBot(stream, html_mt, -1);
/* Send HTML header. */
fputs_P(head, stream);
for (tdp = nutThreadList; tdp; tdp = tdp->td_next) {
fprintf_P(stream, tfmt, (uptr_t) tdp, tdp->td_name, tdp->td_priority,
thread_states[tdp->td_state], (uptr_t) tdp->td_queue, (uptr_t) tdp->td_timer,
(uptr_t) tdp->td_sp, (uptr_t) tdp->td_sp - (uptr_t) tdp->td_memory,
*((u_long *) tdp->td_memory) != DEADBEEF ? "Corr" : "OK");
}
fputs_P(foot, stream);
fflush(stream);
return 0;
}
/*
* CGI Sample: Show list of timers.
*
* This routine must have been registered by NutRegisterCgi() and is
* automatically called by NutHttpProcessRequest() when the client
* request the URL 'cgi-bin/timers.cgi'.
*/
static int ShowTimers(FILE * stream, REQUEST * req)
{
static prog_char head[] = "<HTML><HEAD><TITLE>Timers</TITLE></HEAD><BODY><H1>Timers</H1>\r\n";
static prog_char thead[] =
"<TABLE BORDER><TR><TH>Handle</TH><TH>Countdown</TH><TH>Tick Reload</TH><TH>Callback<BR>Address</TH><TH>Callback<BR>Argument</TH></TR>\r\n";
#if defined(__AVR__)
static prog_char tfmt[] = "<TR><TD>%04X</TD><TD>%lu</TD><TD>%lu</TD><TD>%04X</TD><TD>%04X</TD></TR>\r\n";
#else
static prog_char tfmt[] = "<TR><TD>%08lX</TD><TD>%lu</TD><TD>%lu</TD><TD>%08lX</TD><TD>%08lX</TD></TR>\r\n";
#endif
static prog_char foot[] = "</TABLE></BODY></HTML>";
NUTTIMERINFO *tnp;
u_long ticks_left;
NutHttpSendHeaderTop(stream, req, 200, "Ok");
NutHttpSendHeaderBot(stream, html_mt, -1);
/* Send HTML header. */
fputs_P(head, stream);
if ((tnp = nutTimerList) != 0) {
fputs_P(thead, stream);
ticks_left = 0;
while (tnp) {
ticks_left += tnp->tn_ticks_left;
fprintf_P(stream, tfmt, (uptr_t) tnp, ticks_left, tnp->tn_ticks, (uptr_t) tnp->tn_callback, (uptr_t) tnp->tn_arg);
tnp = tnp->tn_next;
}
}
fputs_P(foot, stream);
fflush(stream);
return 0;
}
/*
* CGI Sample: Show list of sockets.
*
* This routine must have been registered by NutRegisterCgi() and is
* automatically called by NutHttpProcessRequest() when the client
* request the URL 'cgi-bin/sockets.cgi'.
*/
static int ShowSockets(FILE * stream, REQUEST * req)
{
/* String literals are kept in flash ROM. */
static prog_char head[] = "<HTML><HEAD><TITLE>Sockets</TITLE></HEAD>"
"<BODY><H1>Sockets</H1>\r\n"
"<TABLE BORDER><TR><TH>Handle</TH><TH>Type</TH><TH>Local</TH><TH>Remote</TH><TH>Status</TH></TR>\r\n";
#if defined(__AVR__)
static prog_char tfmt1[] = "<TR><TD>%04X</TD><TD>TCP</TD><TD>%s:%u</TD>";
#else
static prog_char tfmt1[] = "<TR><TD>%08lX</TD><TD>TCP</TD><TD>%s:%u</TD>";
#endif
static prog_char tfmt2[] = "<TD>%s:%u</TD><TD>";
static prog_char foot[] = "</TABLE></BODY></HTML>";
static prog_char st_listen[] = "LISTEN";
static prog_char st_synsent[] = "SYNSENT";
static prog_char st_synrcvd[] = "SYNRCVD";
static prog_char st_estab[] = "<FONT COLOR=#CC0000>ESTABL</FONT>";
static prog_char st_finwait1[] = "FINWAIT1";
static prog_char st_finwait2[] = "FINWAIT2";
static prog_char st_closewait[] = "CLOSEWAIT";
static prog_char st_closing[] = "CLOSING";
static prog_char st_lastack[] = "LASTACK";
static prog_char st_timewait[] = "TIMEWAIT";
static prog_char st_closed[] = "CLOSED";
static prog_char st_unknown[] = "UNKNOWN";
prog_char *st_P;
extern TCPSOCKET *tcpSocketList;
TCPSOCKET *ts;
NutHttpSendHeaderTop(stream, req, 200, "Ok");
NutHttpSendHeaderBot(stream, html_mt, -1);
/* Send HTML header. */
fputs_P(head, stream);
for (ts = tcpSocketList; ts; ts = ts->so_next) {
switch (ts->so_state) {
case TCPS_LISTEN:
st_P = (prog_char *) st_listen;
break;
case TCPS_SYN_SENT:
st_P = (prog_char *) st_synsent;
break;
case TCPS_SYN_RECEIVED:
st_P = (prog_char *) st_synrcvd;
break;
case TCPS_ESTABLISHED:
st_P = (prog_char *) st_estab;
break;
case TCPS_FIN_WAIT_1:
st_P = (prog_char *) st_finwait1;
break;
case TCPS_FIN_WAIT_2:
st_P = (prog_char *) st_finwait2;
break;
case TCPS_CLOSE_WAIT:
st_P = (prog_char *) st_closewait;
break;
case TCPS_CLOSING:
st_P = (prog_char *) st_closing;
break;
case TCPS_LAST_ACK:
st_P = (prog_char *) st_lastack;
break;
case TCPS_TIME_WAIT:
st_P = (prog_char *) st_timewait;
break;
case TCPS_CLOSED:
st_P = (prog_char *) st_closed;
break;
default:
st_P = (prog_char *) st_unknown;
break;
}
/*
* Fixed a bug reported by Zhao Weigang.
*/
fprintf_P(stream, tfmt1, (uptr_t) ts, inet_ntoa(ts->so_local_addr), ntohs(ts->so_local_port));
fprintf_P(stream, tfmt2, inet_ntoa(ts->so_remote_addr), ntohs(ts->so_remote_port));
fputs_P(st_P, stream);
fputs("</TD></TR>\r\n", stream);
fflush(stream);
}
fputs_P(foot, stream);
fflush(stream);
return 0;
}
/*
* CGI Sample: Proccessing a form.
*
* This routine must have been registered by NutRegisterCgi() and is
* automatically called by NutHttpProcessRequest() when the client
* request the URL 'cgi-bin/form.cgi'.
*
* Thanks to Tom Boettger, who provided this sample for ICCAVR.
*/
int ShowForm(FILE * stream, REQUEST * req)
{
static prog_char html_head[] = "<HTML><BODY><BR><H1>Form Result</H1><BR><BR>";
static prog_char html_body[] = "<BR><BR><p><a href=\"../index.html\">return to main</a></BODY></HTML></p>";
NutHttpSendHeaderTop(stream, req, 200, "Ok");
NutHttpSendHeaderBot(stream, html_mt, -1);
/* Send HTML header. */
fputs_P(html_head, stream);
if (req->req_query) {
char *name;
char *value;
int i;
int count;
count = NutHttpGetParameterCount(req);
/* Extract count parameters. */
for (i = 0; i < count; i++) {
name = NutHttpGetParameterName(req, i);
value = NutHttpGetParameterValue(req, i);
/* Send the parameters back to the client. */
#ifdef __IMAGECRAFT__
fprintf(stream, "%s: %s<BR>\r\n", name, value);
#else
fprintf_P(stream, PSTR("%s: %s<BR>\r\n"), name, value);
#endif
}
}
fputs_P(html_body, stream);
fflush(stream);
return 0;
}
THREAD(Service, arg)
{
TCPSOCKET *sock;
FILE *stream;
u_char id = (u_char) ((uptr_t) arg);
/*
* Now loop endless for connections.
*/
for (;;) {
/*
* Create a socket.
*/
if ((sock = NutTcpCreateSocket()) == 0) {
printf("[%u] Creating socket failed\n", id);
NutSleep(5000);
continue;
}
/*
* Listen on port 80. This call will block until we get a connection
* from a client.
*/
NutTcpAccept(sock, 80);
#if defined(__AVR__)
printf("[%u] Connected, %u bytes free\n", id, NutHeapAvailable());
#else
printf("[%u] Connected, %lu bytes free\n", id, NutHeapAvailable());
#endif
/*
* Wait until at least 8 kByte of free RAM is available. This will
* keep the client connected in low memory situations.
*/
#if defined(__AVR__)
while (NutHeapAvailable() < 8192) {
#else
while (NutHeapAvailable() < 4096) {
#endif
printf("[%u] Low mem\n", id);
NutSleep(1000);
}
/*
* Associate a stream with the socket so we can use standard I/O calls.
*/
if ((stream = _fdopen((int) ((uptr_t) sock), "r+b")) == 0) {
printf("[%u] Creating stream device failed\n", id);
} else {
/*
* This API call saves us a lot of work. It will parse the
* client's HTTP request, send any requested file from the
* registered file system or handle CGI requests by calling
* our registered CGI routine.
*/
NutHttpProcessRequest(stream);
/*
* Destroy the virtual stream device.
*/
fclose(stream);
}
/*
* Close our socket.
*/
NutTcpCloseSocket(sock);
printf("[%u] Disconnected\n", id);
}
}
int main(void)
{
u_long baud = 115200;
u_char i;
/*
* Initialize the uart device.
*/
NutRegisterDevice(&DEV_DEBUG, 0, 0);
freopen(DEV_DEBUG_NAME, "w", stdout);
_ioctl(_fileno(stdout), UART_SETSPEED, &baud);
NutSleep(200);
printf("\n\nNut/OS %s HTTP Daemon...", NutVersionString());
#ifdef NUTDEBUG
NutTraceTcp(stdout, 0);
NutTraceOs(stdout, 0);
NutTraceHeap(stdout, 0);
NutTracePPP(stdout, 0);
#endif
/*
* Register Ethernet controller.
*/
if (NutRegisterDevice(&DEV_ETHER, 0, 0)) {
puts("Registering device failed");
}
printf("Configure %s...", DEV_ETHER_NAME);
if (NutNetLoadConfig(DEV_ETHER_NAME)) {
u_char mac[] = MY_MAC;
printf("initial boot...");
#ifdef USE_DHCP
if (NutDhcpIfConfig(DEV_ETHER_NAME, mac, 60000))
#endif
{
u_long ip_addr = inet_addr(MY_IPADDR);
u_long ip_mask = inet_addr(MY_IPMASK);
u_long ip_gate = inet_addr(MY_IPGATE);
printf("No DHCP...");
if (NutNetIfConfig(DEV_ETHER_NAME, mac, ip_addr, ip_mask) == 0) {
/* Without DHCP we had to set the default gateway manually.*/
if(ip_gate) {
printf("hard coded gate...");
NutIpRouteAdd(0, 0, ip_gate, &DEV_ETHER);
}
puts("OK");
}
else {
puts("failed");
}
}
}
else {
#ifdef USE_DHCP
if (NutDhcpIfConfig(DEV_ETHER_NAME, 0, 60000)) {
puts("failed");
}
else {
puts("OK");
}
#else
if (NutNetIfConfig(DEV_ETHER_NAME, 0, 0, confnet.cdn_ip_mask)) {
puts("failed");
}
else {
puts("OK");
}
#endif
}
printf("%s ready\n", inet_ntoa(confnet.cdn_ip_addr));
#ifdef USE_DISCOVERY
NutRegisterDiscovery((u_long)-1, 0, DISF_INITAL_ANN);
#endif
/*
* Register our device for the file system.
*/
NutRegisterDevice(&MY_FSDEV, 0, 0);
#ifdef MY_BLKDEV
/* Register block device. */
printf("Registering block device '" MY_BLKDEV_NAME "'...");
if (NutRegisterDevice(&MY_BLKDEV, 0, 0)) {
puts("failed");
for (;;);
}
puts("OK");
/* Mount partition. */
printf("Mounting block device '" MY_BLKDEV_NAME ":1/" MY_FSDEV_NAME "'...");
if (_open(MY_BLKDEV_NAME ":1/" MY_FSDEV_NAME, _O_RDWR | _O_BINARY) == -1) {
puts("failed");
for (;;);
}
puts("OK");
#endif
#ifdef MY_HTTPROOT
/* Register root path. */
printf("Registering HTTP root '" MY_HTTPROOT "'...");
if (NutRegisterHttpRoot(MY_HTTPROOT)) {
puts("failed");
for (;;);
}
puts("OK");
#endif
/*
* Register our CGI sample. This will be called
* by http://host/cgi-bin/test.cgi?anyparams
*/
NutRegisterCgi("test.cgi", ShowQuery);
/*
* Register some CGI samples, which display interesting
* system informations.
*/
NutRegisterCgi("threads.cgi", ShowThreads);
NutRegisterCgi("timers.cgi", ShowTimers);
NutRegisterCgi("sockets.cgi", ShowSockets);
/*
* Finally a CGI example to process a form.
*/
NutRegisterCgi("form.cgi", ShowForm);
/*
* Protect the cgi-bin directory with
* user and password.
*/
NutRegisterAuth("cgi-bin", "root:root");
/*
* Register SSI and ASP handler
*/
NutRegisterSsi();
NutRegisterAsp();
NutRegisterAspCallback(ASPCallback);
/*
* Start four server threads.
*/
for (i = 1; i <= 4; i++) {
char *thname = "httpd0";
thname[5] = '0' + i;
NutThreadCreate(thname, Service, (void *) (uptr_t) i, NUT_THREAD_MAINSTACK);
}
/*
* We could do something useful here, like serving a watchdog.
*/
NutThreadSetPriority(254);
for (;;) {
NutSleep(60000);
}
}
httpd.c Copyright by egnite Software GmbH