Simple multithreaded HTTP daemon.
#include <string.h> #include <dev/nicrtl.h> #include <dev/uartavr.h> #include <sys/thread.h> #include <sys/timer.h> #include <sys/print.h> #include <sys/heap.h> #include <netinet/sostream.h> #include <arpa/inet.h> #include <pro/httpd.h> #ifdef NUTDEBUG #include <sys/osdebug.h> #include <net/netdebug.h> #endif static char *states[] = { "TRM", "<FONT COLOR=#CC0000>RUN</FONT>", "<FONT COLOR=#339966>RDY</FONT>", "SLP" }; /* * CGI Sample. See httpd.h for REQUEST structure. */ static int ShowQuery(NUTDEVICE *sostream, REQUEST *req) { NutHttpSendHeaderTop(sostream, req, 200, "Ok"); NutHttpSendHeaderBot(sostream, "text/html", -1); NutPrintString_P(sostream, PSTR("<HTML>" "<HEAD>" "<TITLE>Show Query</TITLE>" "</HEAD>" "<BODY>")); if(req->req_query) NutPrintString(sostream, req->req_query); else NutPrintString(sostream, "None"); NutPrintString(sostream, "</BODY>" "</HTML>"); NutPrintFlush(sostream); return 0; } /* * CGI Sample. */ static int ShowThreads(NUTDEVICE *sostream, REQUEST *req) { NUTTHREADINFO *tdp = nutThreadList; NutHttpSendHeaderTop(sostream, req, 200, "Ok"); NutHttpSendHeaderBot(sostream, "text/html", -1); NutPrintString_P(sostream, PSTR("<HTML>" "<HEAD>" "<TITLE>Show Threads</TITLE>" "</HEAD>" "<BODY>\r\n")); NutPrintString_P(sostream, PSTR("<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")); while(tdp) { NutPrintFormat(sostream, "<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", tdp, tdp->td_name, tdp->td_priority, states[tdp->td_state], tdp->td_queue, tdp->td_timer, tdp->td_sp, (u_short)tdp->td_sp - (u_short)tdp->td_memory, *((u_long *)tdp->td_memory) != DEADBEEF ? "Corr" : "OK"); tdp = tdp->td_next; } NutPrintString_P(sostream, PSTR("</TABLE></BODY></HTML>")); NutPrintFlush(sostream); return 0; } /* * CGI Sample. */ static int ShowTimer(NUTDEVICE *sostream, REQUEST *req) { NUTTIMERINFO *tnp; u_long ticks_left; NutHttpSendHeaderTop(sostream, req, 200, "Ok"); NutHttpSendHeaderBot(sostream, "text/html", -1); NutPrintString_P(sostream, PSTR("<HTML>" "<HEAD>" "<TITLE>Show Threads</TITLE>" "</HEAD>" "<BODY>")); if((tnp = nutTimerList) != 0) { NutPrintString_P(sostream, PSTR("<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")); ticks_left = 0; while(tnp) { ticks_left += tnp->tn_ticks_left; NutPrintFormat(sostream, "<TR><TD>%04X</TD><TD>%lu</TD><TD>%lu</TD><TD>%04X</TD><TD>%04X</TD></TR>\r\n", tnp, ticks_left, tnp->tn_ticks, tnp->tn_callback, tnp->tn_arg); tnp = tnp->tn_next; } } NutPrintString_P(sostream, PSTR("</TABLE></BODY></HTML>")); NutPrintFlush(sostream); return 0; } /* * CGI Sample. */ static int ShowSockets(NUTDEVICE *sostream, REQUEST *req) { extern TCPSOCKET *tcpSocketList; TCPSOCKET *ts; NutHttpSendHeaderTop(sostream, req, 200, "Ok"); NutHttpSendHeaderBot(sostream, "text/html", -1); NutPrintString_P(sostream, PSTR("<HTML>" "<HEAD>" "<TITLE>Show Threads</TITLE>" "</HEAD>" "<BODY>")); NutPrintString_P(sostream, PSTR("<TABLE BORDER><TR><TH>Handle</TH><TH>Type</TH><TH>Local</TH><TH>Remote</TH><TH>Status</TH></TR>\r\n")); for(ts = tcpSocketList; ts; ts = ts->so_next) { NutPrintFormat(sostream, "<TR><TD>%04X</TD><TD>TCP</TD><TD>%s:%u</TD><TD>%s:%u</TD><TD>", ts, inet_ntoa(ts->so_local_addr), ntohs(ts->so_local_port), inet_ntoa(ts->so_remote_addr), ntohs(ts->so_remote_port)); switch(ts->so_state) { case TCPS_LISTEN: NutPrintString_P(sostream, PSTR("LISTEN")); break; case TCPS_SYN_SENT: NutPrintString_P(sostream, PSTR("SYNSENT")); break; case TCPS_SYN_RECEIVED: NutPrintString_P(sostream, PSTR("SYNRCVD")); break; case TCPS_ESTABLISHED: NutPrintString_P(sostream, PSTR("<FONT COLOR=#CC0000>ESTABL</FONT>")); break; case TCPS_FIN_WAIT_1: NutPrintString_P(sostream, PSTR("FINWAIT1")); break; case TCPS_FIN_WAIT_2: NutPrintString_P(sostream, PSTR("FINWAIT2")); break; case TCPS_CLOSE_WAIT: NutPrintString_P(sostream, PSTR("CLOSEWAIT")); break; case TCPS_CLOSING: NutPrintString_P(sostream, PSTR("CLOSING")); break; case TCPS_LAST_ACK: NutPrintString_P(sostream, PSTR("LASTACK")); break; case TCPS_TIME_WAIT: NutPrintString_P(sostream, PSTR("TIMEWAIT")); break; case TCPS_CLOSED: NutPrintString_P(sostream, PSTR("CLOSED")); break; default: NutPrintString_P(sostream, PSTR("?UNK?")); break; } NutPrintString_P(sostream, PSTR("</TD></TR>\r\n")); NutPrintFlush(sostream); } NutPrintString_P(sostream, PSTR("</TABLE></BODY></HTML>")); NutPrintFlush(sostream); return 0; } THREAD(Service, arg) { TCPSOCKET *sock; NUTDEVICE *sostream; u_char id = (u_char)((u_short)arg); /* * Now loop endless for connections. */ for(;;) { /* * Create a socket. */ if((sock = NutTcpCreateSocket()) == 0) { NutPrintFormat(0, "[%u] Creating socket failed\r\n", id); NutSleep(5000); continue; } /* * Listen on port 80. If we return, * we got a client. */ NutTcpAccept(sock, 80); NutPrintFormat(0, "[%u] Connected, %u bytes free\r\n", id, NutHeapAvailable()); /* * Check for enough memory. If we are below * 8 kByte, take a nap and try again. */ for(;;) { if(NutHeapAvailable() > 8192) break; NutPrintFormat(0, "[%u] Low mem\r\n", id); NutSleep(1000); } /* * Create a virtual stream device from the socket, * so we can use NutPrintFormat() and other stream * device goodies. */ if((sostream = NutSoStreamCreate(sock)) == 0) { NutPrintFormat(0, "[%u] Creating stream device failed\r\n", id); } else { /* * Process http request. */ NutHttpProcessRequest(sostream); /* * Destroy the virtual stream device. */ NutSoStreamDestroy(sostream); } /* * Close our socket. */ NutTcpCloseSocket(sock); NutPrintFormat(0, "[%u] Disconnected\r\n", id); } } THREAD(NutMain, arg) { u_long baud = 115200; u_char i; /* * Initialize the uart device. */ NutDeviceIOCtl(0, UART_SETSPEED, &baud); outp(BV(RXEN) | BV(TXEN), UCR); NutPrintString_P(0, PSTR("\r\nHTTP Daemon...")); #ifdef NUTDEBUG NutKTcpTrace(0); NutKOsTrace(1); NutKHeapTrace(0); #endif /* * Register Realtek controller at address 8300 hex * and interrupt 5. */ if(NutRegisterDevice(&devEth0, 0x8300, 5)) NutPrintString_P(0, PSTR("Registering device failed\r\n")); /* * Configure lan interface. Note that we pass * IP-address and IP-mask as zero, which will * enable DHCP. If you don't got DHCP, call * something like * NutNetIfConfig("eth0", mac, inet_addr("192.168.192.100"), inet_addr("255.255.255.0")); */ if(NutNetAutoConfig("eth0")) NutPrintString_P(0, PSTR("Configuring device failed\r\n")); NutPrintString_P(0, PSTR("ready\r\n")); /* * Register our CGI sample. This will be called * by http://host/cgi-bin/test.cgi?anyparams */ NutRegisterCgi("test.cgi", ShowQuery); /* * Register more CGI samples. */ NutRegisterCgi("threads.cgi", ShowThreads); NutRegisterCgi("timer.cgi", ShowTimer); NutRegisterCgi("sockets.cgi", ShowSockets); /* * Protect the cgi-bin directory with * user and password. */ NutRegisterAuth("cgi-bin", "root:root"); for(i = 1; i <= 4; i++) { char *thname = "httpd0"; thname[5] = '0' + i; NutThreadCreate(thname, Service, (void *)(u_short)i, 640); } /* * We could do something useful here, like serving a watchdog. */ NutThreadSetPriority(254); for(;;) NutSleep(60000); }