Main Page   Modules   Alphabetical List   Data Structures   File List   Data Fields   Globals   Related Pages   Examples  

httpd/httpd.c

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);
}

© 2000-2002 by egnite Software GmbH - visit http://www.ethernut.de/