文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>shttpd源码分析(1) 初识shttpd,大概印象

shttpd源码分析(1) 初识shttpd,大概印象

时间:2010-09-12  来源:雨中漫漫行

最近开始阅读shttpd的源码,这里记录一下自己的理解,欢迎拍砖。

shttpd并不是一个完整的web server,而是提供一些便于实现web server的接口,封装了网络通信等处理细节,并且是跨平台的。我们利用这些接口就可以很容易实现我们想要的web server。

首先来感性认识一下shttpd提供的接口给我们带来的便利性。下面的代码实现了一个简单的web server,是不是很简洁呢。

看main函数中,首先是初始化上下文环境,,接着注册几个处理某个uri的处理回调函数函数、ssi的处理函数、404处理函数,然后就是处理请求的循环,最后是程序结束时的相关资源的回收处理。

下一步就是要看看shttpd提供了哪些接口。

 

/*
 * This file is an example of how to embed web-server functionality
 * into existing application.
 * Compilation line:
 * cc example.c shttpd.c -DEMBEDDED
 */

#ifdef _WIN32
#include <winsock.h>
#define snprintf                        _snprintf

#ifndef _WIN32_WCE
#ifdef _MSC_VER /* pragmas not valid on MinGW */
#pragma comment(lib,"ws2_32")
#endif /* _MSC_VER */
#define ALIAS_URI "/my_c"
#define ALIAS_DIR "c:\\"

#else /* _WIN32_WCE */
/* Windows CE-specific definitions */
#pragma comment(lib,"ws2")
//#include "compat_wince.h"
#define ALIAS_URI "/my_root"
#define ALIAS_DIR "\\"
#endif /* _WIN32_WCE */

#else
#include <sys/types.h>
#include <sys/select.h>
#define ALIAS_URI "/my_etc"
#define ALIAS_DIR "/etc/"
#endif

#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */
#include <time.h>
#include <errno.h>
#include <signal.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

#include "shttpd.h"

/*
 * This callback function is attached to the "/" and "/abc.html" URIs,
 * thus is acting as "index.html" file. It shows a bunch of links
 * to other URIs, and allows to change the value of program's
 * internal variable. The pointer to that variable is passed to the
 * callback function as arg->user_data.
 */
static void
show_index(struct shttpd_arg *arg)
{
        int             *p = arg->user_data; /* integer passed to us */
        char            value[20];
        const char      *host, *request_method, *query_string, *request_uri;

        request_method = shttpd_get_env(arg, "REQUEST_METHOD");
        request_uri = shttpd_get_env(arg, "REQUEST_URI");
        query_string = shttpd_get_env(arg, "QUERY_STRING");

        /* Change the value of integer variable */
        value[0] = '\0';
        if (!strcmp(request_method, "POST")) {
                /* If not all data is POSTed, wait for the rest */
                if (arg->flags & SHTTPD_MORE_POST_DATA)
                        return;
                (void) shttpd_get_var("name1", arg->in.buf, arg->in.len,
                    value, sizeof(value));
        } else if (query_string != NULL) {
                (void) shttpd_get_var("name1", query_string,
                    strlen(query_string), value, sizeof(value));
        }
        if (value[0] != '\0') {
                *p = atoi(value);

                /*
                 * Suggested by Luke Dunstan. When POST is used,
                 * send 303 code to force the browser to re-request the
                 * page using GET method. This prevents the possibility of
                 * the user accidentally resubmitting the form when using
                 * Refresh or Back commands in the browser.
                 */
                if (!strcmp(request_method, "POST")) {
                        shttpd_printf(arg, "HTTP/1.1 303 See Other\r\n"
                                "Location: %s\r\n\r\n", request_uri);
                        arg->flags |= SHTTPD_END_OF_OUTPUT;
                        return;
                }
        }

        shttpd_printf(arg, "%s",
                "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
                "<html><body><h1>Welcome to embedded example of SHTTPD");
        shttpd_printf(arg, " v. %s </h1><ul>", shttpd_version());

        shttpd_printf(arg, "<li><code>REQUEST_METHOD: %s "
            "REQUEST_URI: \"%s\" QUERY_STRING: \"%s\""
            " REMOTE_ADDR: %s REMOTE_USER: \"(null)\"</code><hr>",
            request_method, request_uri,
            query_string ? query_string : "(null)",
            shttpd_get_env(arg, "REMOTE_ADDR"));
        shttpd_printf(arg, "<li>Internal int variable value: <b>%d</b>", *p);

        shttpd_printf(arg, "%s",
                "<form method=\"GET\">Enter new value: "
                "<input type=\"text\" name=\"name1\"/>"
                "<input type=\"submit\" "
                "value=\"set new value using GET method\"></form>");
        shttpd_printf(arg, "%s",
                "<form method=\"POST\">Enter new value: "
                "<input type=\"text\" name=\"name1\"/>"
                "<input type=\"submit\" "
                "value=\"set new value using POST method\"></form>");
                
        shttpd_printf(arg, "%s",
                "<hr><li><a href=\"/secret\">"
                "Protected page</a> (guest:guest)<hr>"
                "<li><a href=\"/huge\">Output lots of data</a><hr>"
                "<li><a href=\"" ALIAS_URI "/\">Aliased " ALIAS_DIR " directory</a><hr>");
        shttpd_printf(arg, "%s",
                "<li><a href=\"/Makefile\">On-disk file (Makefile)</a><hr>"
                "<li><a href=\"/users/joe/\">Wildcard URI example</a><hr>"
                "<li><a href=\"/not-existent/\">Custom 404 handler</a><hr>");

        host = shttpd_get_header(arg, "Host");
        shttpd_printf(arg, "<li>'Host' header value: [%s]<hr>",
            host ? host : "NOT SET");

        shttpd_printf(arg, "<li>Upload file example. "
            "<form method=\"post\" enctype=\"multipart/form-data\" "
            "action=\"/post\"><input type=\"file\" name=\"file\">"
            "<input type=\"submit\"></form>");

        shttpd_printf(arg, "%s", "</body></html>");
        arg->flags |= SHTTPD_END_OF_OUTPUT;
}

/*
 * This callback is attached to the URI "/post"
 * It uploads file from a client to the server. This is the demostration
 * of how to use POST method to send lots of data from the client.
 * The uploaded file is saved into "uploaded.txt".
 * This function is called many times during single request. To keep the
 * state (how many bytes we have received, opened file etc), we allocate
 * a "struct state" structure for every new connection.
 */
static void
show_post(struct shttpd_arg *arg)
{
        const char      *s, *path = "uploaded.txt";
        struct state {
                size_t  cl;             /* Content-Length       */
                size_t  nread;          /* Number of bytes read */
                FILE    *fp;
        } *state;

        /* If the connection was broken prematurely, cleanup */
        if (arg->flags & SHTTPD_CONNECTION_ERROR && arg->state) {
                (void) fclose(((struct state *) arg->state)->fp);
                free(arg->state);
        } else if ((s = shttpd_get_header(arg, "Content-Length")) == NULL) {
                shttpd_printf(arg, "HTTP/1.0 411 Length Required\n\n");
                arg->flags |= SHTTPD_END_OF_OUTPUT;
        } else if (arg->state == NULL) {
                /* New request. Allocate a state structure, and open a file */
                arg->state = state = calloc(1, sizeof(*state));
                state->cl = strtoul(s, NULL, 10);
                state->fp = fopen(path, "wb+");
                shttpd_printf(arg, "HTTP/1.0 200 OK\n"
                        "Content-Type: text/plain\n\n");
        } else {
                state = arg->state;

                /*
                 * Write the POST data to a file. We do not do any URL
                 * decoding here. File will contain form-urlencoded stuff.
                 */
                (void) fwrite(arg->in.buf, arg->in.len, 1, state->fp);
                state->nread += arg->in.len;

                /* Tell SHTTPD we have processed all data */
                arg->in.num_bytes = arg->in.len;

                /* Data stream finished? Close the file, and free the state */
                if (state->nread >= state->cl) {
                        shttpd_printf(arg, "Written %d bytes to %s",
                            state->nread, path);
                        (void) fclose(state->fp);
                        free(state);
                        arg->flags |= SHTTPD_END_OF_OUTPUT;
                }
        }
}

/*
 * This callback function is attached to the "/secret" URI.
 * It shows simple text message, but in order to be shown, user must
 * authorized himself against the passwords file "passfile".
 */
static void
show_secret(struct shttpd_arg *arg)
{
        shttpd_printf(arg, "%s", "HTTP/1.1 200 OK\r\n");
        shttpd_printf(arg, "%s", "Content-Type: text/html\r\n\r\n");
        shttpd_printf(arg, "%s", "<html><body>");
        shttpd_printf(arg, "%s", "<p>This is a protected page</body></html>");
        arg->flags |= SHTTPD_END_OF_OUTPUT;
}

/*
 * This callback function is attached to the "/huge" URI.
 * It outputs binary data to the client.
 * The number of bytes already sent is stored directly in the arg->state.
 */
static void
show_huge(struct shttpd_arg *arg)
{
        int             state = (int) arg->state;

        if (state == 0) {
                shttpd_printf(arg, "%s", "HTTP/1.1 200 OK\r\n");
                shttpd_printf(arg, "%s", "Content-Type: text/plain\r\n\r\n");
        }

        while (arg->out.num_bytes < arg->out.len) {
                arg->out.buf[arg->out.num_bytes] = state % 72 ? 'A' : '\n';
                arg->out.num_bytes++;
                state++;

                if (state > 1024 * 1024) {   /* Output 1Mb Kb of data */
                        arg->flags |= SHTTPD_END_OF_OUTPUT;
                        break;
                }
        }
        
        arg->state = (void *) state;
}

/*
 * This callback function is used to show how to handle 404 error
 */
static void
show_404(struct shttpd_arg *arg)
{
        shttpd_printf(arg, "%s", "HTTP/1.1 200 OK\r\n");
        shttpd_printf(arg, "%s", "Content-Type: text/plain\r\n\r\n");
        shttpd_printf(arg, "%s", "Oops. File not found! ");
        shttpd_printf(arg, "%s", "This is a custom error handler.");
        arg->flags |= SHTTPD_END_OF_OUTPUT;
}

/*
 * This callback function is attached to the wildcard URI "/users/.*"
 * It shows a greeting message and an actual URI requested by the user.
 */
static void
show_users(struct shttpd_arg *arg)
{
        shttpd_printf(arg, "%s", "HTTP/1.1 200 OK\r\n");
        shttpd_printf(arg, "%s", "Content-Type: text/html\r\n\r\n");
        shttpd_printf(arg, "%s", "<html><body>");
        shttpd_printf(arg, "%s", "<h1>Hi. This is a wildcard uri handler"
            "for the URI /users/*/ </h1>");
        shttpd_printf(arg, "<h2>URI: %s</h2></body></html>",
                shttpd_get_env(arg, "REQUEST_URI"));
        arg->flags |= SHTTPD_END_OF_OUTPUT;
}

/*
 * This function will be called on SSI directive <!--#if true -->, or
 * <!--#elif true -->, and 'returns' TRUE condition
 */
static void
ssi_test_true(struct shttpd_arg *arg)
{
        arg->flags |= SHTTPD_SSI_EVAL_TRUE;
}

/*
 * This function will be called on SSI directive <!--#if false -->, or
 * <!--#elif false -->, and 'returns' FALSE condition
 */
static void
ssi_test_false(struct shttpd_arg *arg)
{
        /* Do not set SHTTPD_SSI_EVAL_TRUE flag, that means FALSE */
}

/*
 * This function will be called on SSI <!--#call print_stuff -->
 */
static void
ssi_print_stuff(struct shttpd_arg *arg)
{
        time_t  t = time(NULL);

        shttpd_printf(arg,
            "SSI user callback output: Current time: %s", ctime(&t));
        arg->flags |= SHTTPD_END_OF_OUTPUT;
}

int main(int argc, char *argv[])
{
        int                     data = 1234567;
        struct shttpd_ctx       *ctx;
        
        /* Get rid of warnings */
        argc = argc;
        argv = argv;

#ifndef _WIN32
        signal(SIGPIPE, SIG_IGN);
#endif /* !_WIN32 */

        /*
         * Initialize SHTTPD context.
         * Attach folder c:\ to the URL /my_c  (for windows), and
         * /etc/ to URL /my_etc (for UNIX). These are Apache-like aliases.
         * Set WWW root to current directory.
         * Start listening on ports 8080 and 8081
         */
        ctx = shttpd_init(NULL,
                "listen_ports", "8080,8081",
                "aliases", ALIAS_URI "=" ALIAS_DIR,
                "document_root", ".", NULL);

        /* Register an index page under two URIs */
        shttpd_register_uri(ctx, "/", &show_index, (void *) &data);
        shttpd_register_uri(ctx, "/abc.html", &show_index, (void *) &data);

        /* Register a callback on wildcard URI */
        shttpd_register_uri(ctx, "/users/*/", &show_users, NULL);

        /* Show how to use password protection */
        shttpd_register_uri(ctx, "/secret", &show_secret, NULL);
        shttpd_protect_uri(ctx, "/secret", "passfile");

        /* Show how to use stateful big data transfer */
        shttpd_register_uri(ctx, "/huge", &show_huge, NULL);

        /* Register URI for file upload */
        shttpd_register_uri(ctx, "/post", &show_post, NULL);

        /* Register SSI callbacks */
        shttpd_register_ssi_func(ctx, "true", ssi_test_true, NULL);
        shttpd_register_ssi_func(ctx, "false", ssi_test_false, NULL);
        shttpd_register_ssi_func(ctx, "print_stuff", ssi_print_stuff, NULL);

        shttpd_handle_error(ctx, 404, show_404, NULL);

        /* Serve connections infinitely until someone kills us */
        for (;;)
                shttpd_poll(ctx, 1000);

        /* Probably unreached, because we will be killed by a signal */
        shttpd_fini(ctx);

        return (EXIT_SUCCESS);
}


相关阅读 更多 +
排行榜 更多 +
胜利女神新的希望小米服手游下载

胜利女神新的希望小米服手游下载

角色扮演 下载
我要当老板伐木工厂游戏下载

我要当老板伐木工厂游戏下载

模拟经营 下载
涡轮螺旋桨最新版下载

涡轮螺旋桨最新版下载

模拟经营 下载