├── test
├── wwwroot
│ ├── coffee.jpg
│ ├── copyright.jpg
│ ├── copyright.png
│ ├── happynewyear2014.jpg
│ ├── Pawn_Language_Guide.pdf
│ ├── happynewyear2014.html
│ ├── copyright.html
│ └── index.html
├── main1.c
├── main2.c
└── main3.c
├── tinyweb1.h
├── tinyweb2.h
├── tinyweb3.h
├── README.md
├── win32
├── tinyweb.dsw
├── tinyweb1.dsp
├── tinyweb2.dsp
└── tinyweb3.dsp
├── tinyweb1.c
├── membuf.h
├── membuf.c
├── tinyweb2.c
└── tinyweb3.c
/test/wwwroot/coffee.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liigo/tinyweb/HEAD/test/wwwroot/coffee.jpg
--------------------------------------------------------------------------------
/test/wwwroot/copyright.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liigo/tinyweb/HEAD/test/wwwroot/copyright.jpg
--------------------------------------------------------------------------------
/test/wwwroot/copyright.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liigo/tinyweb/HEAD/test/wwwroot/copyright.png
--------------------------------------------------------------------------------
/test/wwwroot/happynewyear2014.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liigo/tinyweb/HEAD/test/wwwroot/happynewyear2014.jpg
--------------------------------------------------------------------------------
/test/wwwroot/Pawn_Language_Guide.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liigo/tinyweb/HEAD/test/wwwroot/Pawn_Language_Guide.pdf
--------------------------------------------------------------------------------
/test/wwwroot/happynewyear2014.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Happy new year 2014!
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/tinyweb1.h:
--------------------------------------------------------------------------------
1 | #ifndef __TINYWEB_H__
2 | #define __TINYWEB_H__
3 |
4 | #include
5 |
6 | void tinyweb_start(uv_loop_t* loop, const char* ip, int port);
7 |
8 | #endif //__TINYWEB_H__
9 |
--------------------------------------------------------------------------------
/tinyweb2.h:
--------------------------------------------------------------------------------
1 | #ifndef __TINYWEB_H__
2 | #define __TINYWEB_H__
3 |
4 | #include
5 |
6 | void tinyweb_start(uv_loop_t* loop, const char* ip, int port);
7 |
8 | #endif //__TINYWEB_H__
9 |
--------------------------------------------------------------------------------
/test/wwwroot/copyright.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Copyright 2013-2014, by Liigo.
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/tinyweb3.h:
--------------------------------------------------------------------------------
1 | #ifndef __TINYWEB_H__
2 | #define __TINYWEB_H__
3 |
4 | #include
5 |
6 | void tinyweb_start(uv_loop_t* loop, const char* ip, int port, const char* doc_root_path);
7 |
8 | #endif //__TINYWEB_H__
9 |
--------------------------------------------------------------------------------
/test/main1.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "../tinyweb1.h"
5 |
6 | int main()
7 | {
8 | tinyweb_start(uv_default_loop(), "127.0.0.1", 8080);
9 | uv_run(uv_default_loop(), UV_RUN_DEFAULT);
10 | }
11 |
--------------------------------------------------------------------------------
/test/main2.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "../tinyweb2.h"
5 |
6 | int main()
7 | {
8 | tinyweb_start(uv_default_loop(), "127.0.0.1", 8080);
9 | uv_run(uv_default_loop(), UV_RUN_DEFAULT);
10 | }
11 |
--------------------------------------------------------------------------------
/test/main3.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "../tinyweb3.h"
5 |
6 | int main()
7 | {
8 | tinyweb_start(uv_default_loop(), "127.0.0.1", 8080, "D:\\Liigo\\github\\tinyweb\\test\\wwwroot");
9 | uv_run(uv_default_loop(), UV_RUN_DEFAULT);
10 | }
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | tinyweb
2 | =======
3 |
4 | Tinyweb, a tiny web server based on libuv, by liigo, 2013/06.
5 |
6 | Tinyweb 是我用libuv开发的一个最精简的Web server服务器。分为三个版本,都是从真实项目中剥离出来的,从 v1 到 v2 到 v3,就是一个Web server从雏形到基本功能逐渐丰富的过程。
7 |
8 | tinyweb v1,是最基础的libuv的hello world,可用来学习libuv的入门用法;tinyweb v2,在v1的基础上,添加了解析HTTP GET请求(Request)提取`path_info`和`query_string`并发送回复(Respone)的功能;tinyweb v3,在v2的基础上,又添加了对静态文件的支持。
9 |
10 | 真正在项目中有实用价值的,我认为应该是从tinyweb v2开始引入的对`path_info`的响应处理:在项目中嵌入tinyweb服务器,响应特定`path_info`,或输出内部运行状态,或触发某个动作,如此一来,用户(或开发者自己)通过Web浏览器即可轻松完成与项目程序的有效沟通,至少免除了进程通讯之类的东西吧,通过特殊的`path_info`(比如`http://localhost/hibos`)给自己的程序留一个小小的后门也是轻而易举。
11 |
12 | CSDN博客文章链接:
13 |
14 | - [基于libuv的最精简Web服务器:tinyweb v1 v2 v3(C语言源码)](http://blog.csdn.net/liigo/article/details/38424417)
15 | - [tinyweb: C语言 + libuv 开发的最精简的WebServer(附源码)](http://blog.csdn.net/liigo/article/details/9149415)
16 |
--------------------------------------------------------------------------------
/test/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Welcome to tinyweb
4 | This is the home page, for tinyweb v3.
5 |
6 | Link to files:
7 | index.html
8 | copyright.html
9 | happynewyear2014.html
10 |
11 | Link to actions:
12 | /404
13 | /user/query?author=liigo
14 | /any/other/pathinfo?query=strings&id=123
15 |
16 | Image:
17 | 
18 |
19 | Octet stream:
20 | Pawn_Language_Guide.pdf
21 | CookTime.apk (Remote link)
22 |
23 |
24 |
--------------------------------------------------------------------------------
/win32/tinyweb.dsw:
--------------------------------------------------------------------------------
1 | Microsoft Developer Studio Workspace File, Format Version 6.00
2 | # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
3 |
4 | ###############################################################################
5 |
6 | Project: "tinyweb1"=.\tinyweb1.dsp - Package Owner=<4>
7 |
8 | Package=<5>
9 | {{{
10 | }}}
11 |
12 | Package=<4>
13 | {{{
14 | }}}
15 |
16 | ###############################################################################
17 |
18 | Project: "tinyweb2"=.\tinyweb2.dsp - Package Owner=<4>
19 |
20 | Package=<5>
21 | {{{
22 | }}}
23 |
24 | Package=<4>
25 | {{{
26 | }}}
27 |
28 | ###############################################################################
29 |
30 | Project: "tinyweb3"=.\tinyweb3.dsp - Package Owner=<4>
31 |
32 | Package=<5>
33 | {{{
34 | }}}
35 |
36 | Package=<4>
37 | {{{
38 | }}}
39 |
40 | ###############################################################################
41 |
42 | Global:
43 |
44 | Package=<5>
45 | {{{
46 | }}}
47 |
48 | Package=<3>
49 | {{{
50 | }}}
51 |
52 | ###############################################################################
53 |
54 |
--------------------------------------------------------------------------------
/tinyweb1.c:
--------------------------------------------------------------------------------
1 | #include "tinyweb1.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | // Tinyweb v1, a tiny web server based on libuv, by liigo, 2013-6-20.
10 |
11 | uv_tcp_t _server;
12 | uv_tcp_t _client;
13 | uv_loop_t* _loop;
14 |
15 | static void tinyweb_on_connection(uv_stream_t* server, int status);
16 |
17 | //start web server, linstening ip:port
18 | //ip can be NULL or "", which means "0.0.0.0"
19 | void tinyweb_start(uv_loop_t* loop, const char* ip, int port) {
20 | struct sockaddr_in addr;
21 | uv_ip4_addr((ip && ip[0]) ? ip : "0.0.0.0", port, &addr);
22 | _loop = loop;
23 | uv_tcp_init(_loop, &_server);
24 | uv_tcp_bind(&_server, (const struct sockaddr*) &addr, 0);
25 | uv_listen((uv_stream_t*)&_server, 8, tinyweb_on_connection);
26 | }
27 |
28 | static void after_uv_close(uv_handle_t* handle) {
29 | free(handle); // uv_tcp_t* client, see tinyweb_on_connection()
30 | }
31 |
32 | static void after_uv_write(uv_write_t* w, int status) {
33 | if(w->data)
34 | free(w->data);
35 | uv_close((uv_handle_t*)w->handle, after_uv_close); // close client
36 | free(w);
37 | }
38 |
39 | static void write_uv_data(uv_stream_t* stream, const void* data, unsigned int len, int need_copy_data) {
40 | uv_buf_t buf;
41 | uv_write_t* w;
42 | void* newdata = (void*)data;
43 |
44 | if(data == NULL || len == 0) return;
45 | if(len ==(unsigned int)-1)
46 | len = strlen(data);
47 |
48 | if(need_copy_data) {
49 | newdata = malloc(len);
50 | memcpy(newdata, data, len);
51 | }
52 |
53 | buf = uv_buf_init(newdata, len);
54 | w = (uv_write_t*)malloc(sizeof(uv_write_t));
55 | w->data = need_copy_data ? newdata : NULL;
56 | uv_write(w, stream, &buf, 1, after_uv_write); // free w and w->data in after_uv_write()
57 | }
58 |
59 | static const char* http_respone = "HTTP/1.1 200 OK\r\n"
60 | "Content-Type:text/html;charset=utf-8\r\n"
61 | "Content-Length:18\r\n"
62 | "\r\n"
63 | "Welcome to tinyweb";
64 |
65 | static void tinyweb_on_connection(uv_stream_t* server, int status) {
66 | assert(server == (uv_stream_t*)&_server);
67 | if(status == 0) {
68 | uv_tcp_t* client = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));
69 | uv_tcp_init(_loop, client);
70 | uv_accept((uv_stream_t*)&_server, (uv_stream_t*)client);
71 | write_uv_data((uv_stream_t*)client, http_respone, -1, 0);
72 | //close client after uv_write, and free it in after_uv_close()
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/membuf.h:
--------------------------------------------------------------------------------
1 | #ifndef __MEMBUF_H__
2 | #define __MEMBUF_H__
3 |
4 | // `membuf_t` is a growable continuous in-memory buffer.
5 | // It also support "local buffer" to use stack memory efficiently.
6 | // https://github.com/liigo/membuf
7 | // by Liigo, 2013-7-5, 2014-8-16, 2014-10-21, 2014-11-18.
8 |
9 | #include
10 |
11 | typedef struct {
12 | unsigned char* data;
13 | unsigned int size;
14 | unsigned int buffer_size;
15 | unsigned char uses_local_buffer; // local buffer, e.g. on stack
16 | } membuf_t;
17 |
18 | #ifndef MEMBUF_INIT_LOCAL
19 | #define MEMBUF_INIT_LOCAL(buf,n) membuf_t buf; unsigned char buf##n[n]; membuf_init_local(&buf, &buf##n, n);
20 | #endif
21 |
22 | void membuf_init(membuf_t* buf, unsigned int initial_buffer_size);
23 | void membuf_init_local(membuf_t* buf, void* local_buffer, unsigned int local_buffer_size);
24 | void membuf_init_move_from(membuf_t* buf, membuf_t* other); // don't use other anymore
25 | void membuf_uninit(membuf_t* buf);
26 |
27 | unsigned int membuf_append_data(membuf_t* buf, void* data, unsigned int size);
28 | unsigned int membuf_append_zeros(membuf_t* buf, unsigned int size);
29 | unsigned int membuf_append_text(membuf_t* buf, const char* str, unsigned int len);
30 | unsigned int membuf_append_text_zero(membuf_t* buf, const char* str, unsigned int len);
31 |
32 | static void* membuf_get_data(membuf_t* buf) { return (buf->size == 0 ? NULL : buf->data); }
33 | static unsigned int membuf_get_size(membuf_t* buf) { return buf->size; }
34 | static unsigned int membuf_is_empty(membuf_t* buf) { return buf->size > 0; }
35 | static void membuf_empty(membuf_t* buf) { buf->size = 0; }
36 |
37 | void membuf_reserve(membuf_t* buf, unsigned int extra_size);
38 | void* membuf_detach(membuf_t* buf, unsigned int* psize); // need free() result if not NULL
39 |
40 | #if defined(_MSC_VER)
41 | #define MEMBUF_INLINE static _inline
42 | #else
43 | #define MEMBUF_INLINE static inline
44 | #endif
45 |
46 | MEMBUF_INLINE unsigned int membuf_append_byte(membuf_t* buf, unsigned char b) {
47 | return membuf_append_data(buf, &b, sizeof(b));
48 | }
49 | MEMBUF_INLINE unsigned int membuf_append_int(membuf_t* buf, int i) {
50 | return membuf_append_data(buf, &i, sizeof(i));
51 | }
52 | MEMBUF_INLINE unsigned int membuf_append_uint(membuf_t* buf, unsigned int ui) {
53 | return membuf_append_data(buf, &ui, sizeof(ui));
54 | }
55 | MEMBUF_INLINE unsigned int membuf_append_short(membuf_t* buf, short s) {
56 | return membuf_append_data(buf, &s, sizeof(s));
57 | }
58 | MEMBUF_INLINE unsigned int membuf_append_ushort(membuf_t* buf, unsigned short us) {
59 | return membuf_append_data(buf, &us, sizeof(us));
60 | }
61 | MEMBUF_INLINE unsigned int membuf_append_float(membuf_t* buf, float f) {
62 | return membuf_append_data(buf, &f, sizeof(f));
63 | }
64 | MEMBUF_INLINE unsigned int membuf_append_double(membuf_t* buf, double d) {
65 | return membuf_append_data(buf, &d, sizeof(d));
66 | }
67 | MEMBUF_INLINE unsigned int membuf_append_ptr(membuf_t* buf, void* ptr) {
68 | return membuf_append_data(buf, &ptr, sizeof(ptr));
69 | }
70 |
71 | #endif //__MEMBUF_H__
72 |
--------------------------------------------------------------------------------
/membuf.c:
--------------------------------------------------------------------------------
1 | #include "membuf.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | // https://github.com/liigo/membuf
9 | // by Liigo.
10 |
11 | void membuf_init(membuf_t* buf, unsigned int initial_buffer_size) {
12 | memset(buf, 0, sizeof(membuf_t));
13 | buf->data = initial_buffer_size > 0 ? (unsigned char*) malloc(initial_buffer_size) : NULL;
14 | buf->buffer_size = initial_buffer_size;
15 | buf->uses_local_buffer = 0;
16 | }
17 |
18 | void membuf_init_local(membuf_t* buf, void* local_buffer, unsigned int local_buffer_size) {
19 | memset(buf, 0, sizeof(membuf_t));
20 | buf->data = (local_buffer && (local_buffer_size > 0)) ? local_buffer : NULL;
21 | buf->buffer_size = local_buffer_size;
22 | buf->uses_local_buffer = 1;
23 | }
24 |
25 | void membuf_init_move_from(membuf_t* buf, membuf_t* other) {
26 | if(other->uses_local_buffer) {
27 | membuf_init(buf, 0);
28 | if(other->size > 0)
29 | membuf_append_data(buf, other->data, other->size);
30 | } else {
31 | *buf = *other;
32 | }
33 | memset(other, 0, sizeof(membuf_t)); // other is hollowed now
34 | }
35 |
36 | void membuf_uninit(membuf_t* buf) {
37 | if(!buf->uses_local_buffer && buf->data)
38 | free(buf->data);
39 | memset(buf, 0, sizeof(membuf_t));
40 | }
41 |
42 | void membuf_reserve(membuf_t* buf, unsigned int extra_size) {
43 | if(extra_size > buf->buffer_size - buf->size) {
44 | //calculate new buffer size
45 | unsigned int new_buffer_size = buf->buffer_size == 0 ? extra_size : buf->buffer_size << 1;
46 | unsigned int new_data_size = buf->size + extra_size;
47 | while(new_buffer_size < new_data_size)
48 | new_buffer_size <<= 1;
49 |
50 | // malloc/realloc new buffer
51 | if(buf->uses_local_buffer) {
52 | void* local = buf->data;
53 | buf->data = realloc(NULL, new_buffer_size); // alloc new buffer
54 | memcpy(buf->data, local, buf->size); // copy local bufer to new buffer
55 | buf->uses_local_buffer = 0;
56 | } else {
57 | buf->data = realloc(buf->data, new_buffer_size); // realloc new buffer
58 | }
59 | buf->buffer_size = new_buffer_size;
60 | }
61 | }
62 |
63 | unsigned int membuf_append_data(membuf_t* buf, void* data, unsigned int size) {
64 | assert(data && size > 0);
65 | membuf_reserve(buf, size);
66 | memmove(buf->data + buf->size, data, size);
67 | buf->size += size;
68 | return (buf->size - size);
69 | }
70 |
71 | unsigned int membuf_append_zeros(membuf_t* buf, unsigned int size) {
72 | membuf_reserve(buf, size);
73 | memset(buf->data + buf->size, 0, size);
74 | buf->size += size;
75 | return (buf->size - size);
76 | }
77 |
78 | unsigned int membuf_append_text(membuf_t* buf, const char* str, unsigned int len) {
79 | if(str && (len == (unsigned int)(-1)))
80 | len = strlen(str);
81 | return membuf_append_data(buf, (void*)str, len);
82 | }
83 |
84 | unsigned int membuf_append_text_zero(membuf_t* buf, const char* str, unsigned int len) {
85 | unsigned int offset;
86 | if(str && (len == (unsigned int)(-1)))
87 | len = strlen(str);
88 | membuf_reserve(buf, len + 1);
89 | offset = membuf_append_data(buf, (void*)str, len);
90 | membuf_append_zeros(buf, 1);
91 | return offset;
92 | }
93 |
94 | void* membuf_detach(membuf_t* buf, unsigned int* psize) {
95 | void* result = buf->data;
96 | if(psize) *psize = buf->size;
97 | if(buf->uses_local_buffer) {
98 | result = buf->size > 0 ? malloc(buf->size) : NULL;
99 | memcpy(result, buf->data, buf->size);
100 | } else {
101 | buf->buffer_size = 0;
102 | }
103 | buf->data = NULL;
104 | buf->size = 0;
105 | return result;
106 | }
107 |
108 | MEMBUF_INLINE void swap_data(membuf_t* buf1, membuf_t* buf2) {
109 | unsigned char* tmp_data = buf1->data;
110 | buf1->data = buf2->data; buf2->data = tmp_data;
111 | }
112 |
113 | MEMBUF_INLINE void swap_size(membuf_t* buf1, membuf_t* buf2) {
114 | unsigned int tmp_size = buf1->size;
115 | buf1->size = buf2->size; buf2->size = tmp_size;
116 | }
117 |
118 | MEMBUF_INLINE void swap_buffer_size(membuf_t* buf1, membuf_t* buf2) {
119 | unsigned int tmp_buffer_size = buf1->buffer_size;
120 | buf1->buffer_size = buf2->buffer_size; buf2->buffer_size = tmp_buffer_size;
121 | }
122 |
--------------------------------------------------------------------------------
/win32/tinyweb1.dsp:
--------------------------------------------------------------------------------
1 | # Microsoft Developer Studio Project File - Name="tinyweb1" - Package Owner=<4>
2 | # Microsoft Developer Studio Generated Build File, Format Version 6.00
3 | # ** DO NOT EDIT **
4 |
5 | # TARGTYPE "Win32 (x86) Console Application" 0x0103
6 |
7 | CFG=tinyweb1 - Win32 Debug
8 | !MESSAGE This is not a valid makefile. To build this project using NMAKE,
9 | !MESSAGE use the Export Makefile command and run
10 | !MESSAGE
11 | !MESSAGE NMAKE /f "tinyweb1.mak".
12 | !MESSAGE
13 | !MESSAGE You can specify a configuration when running NMAKE
14 | !MESSAGE by defining the macro CFG on the command line. For example:
15 | !MESSAGE
16 | !MESSAGE NMAKE /f "tinyweb1.mak" CFG="tinyweb1 - Win32 Debug"
17 | !MESSAGE
18 | !MESSAGE Possible choices for configuration are:
19 | !MESSAGE
20 | !MESSAGE "tinyweb1 - Win32 Release" (based on "Win32 (x86) Console Application")
21 | !MESSAGE "tinyweb1 - Win32 Debug" (based on "Win32 (x86) Console Application")
22 | !MESSAGE
23 |
24 | # Begin Project
25 | # PROP AllowPerConfigDependencies 0
26 | # PROP Scc_ProjName ""
27 | # PROP Scc_LocalPath ""
28 | CPP=cl.exe
29 | RSC=rc.exe
30 |
31 | !IF "$(CFG)" == "tinyweb1 - Win32 Release"
32 |
33 | # PROP BASE Use_MFC 0
34 | # PROP BASE Use_Debug_Libraries 0
35 | # PROP BASE Output_Dir "Release"
36 | # PROP BASE Intermediate_Dir "Release"
37 | # PROP BASE Target_Dir ""
38 | # PROP Use_MFC 0
39 | # PROP Use_Debug_Libraries 0
40 | # PROP Output_Dir "Release"
41 | # PROP Intermediate_Dir "Release"
42 | # PROP Ignore_Export_Lib 0
43 | # PROP Target_Dir ""
44 | # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
45 | # ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
46 | # ADD BASE RSC /l 0x804 /d "NDEBUG"
47 | # ADD RSC /l 0x804 /d "NDEBUG"
48 | BSC32=bscmake.exe
49 | # ADD BASE BSC32 /nologo
50 | # ADD BSC32 /nologo
51 | LINK32=link.exe
52 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
53 | # ADD LINK32 libuv.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
54 |
55 | !ELSEIF "$(CFG)" == "tinyweb1 - Win32 Debug"
56 |
57 | # PROP BASE Use_MFC 0
58 | # PROP BASE Use_Debug_Libraries 1
59 | # PROP BASE Output_Dir "Debug"
60 | # PROP BASE Intermediate_Dir "Debug"
61 | # PROP BASE Target_Dir ""
62 | # PROP Use_MFC 0
63 | # PROP Use_Debug_Libraries 1
64 | # PROP Output_Dir "Debug"
65 | # PROP Intermediate_Dir "Debug"
66 | # PROP Ignore_Export_Lib 0
67 | # PROP Target_Dir ""
68 | # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
69 | # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
70 | # ADD BASE RSC /l 0x804 /d "_DEBUG"
71 | # ADD RSC /l 0x804 /d "_DEBUG"
72 | BSC32=bscmake.exe
73 | # ADD BASE BSC32 /nologo
74 | # ADD BSC32 /nologo
75 | LINK32=link.exe
76 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
77 | # ADD LINK32 libuvd.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
78 |
79 | !ENDIF
80 |
81 | # Begin Target
82 |
83 | # Name "tinyweb1 - Win32 Release"
84 | # Name "tinyweb1 - Win32 Debug"
85 | # Begin Group "Source Files"
86 |
87 | # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
88 | # Begin Source File
89 |
90 | SOURCE=..\test\main1.c
91 | # End Source File
92 | # Begin Source File
93 |
94 | SOURCE=..\tinyweb1.c
95 | # End Source File
96 | # End Group
97 | # Begin Group "Header Files"
98 |
99 | # PROP Default_Filter "h;hpp;hxx;hm;inl"
100 | # Begin Source File
101 |
102 | SOURCE=..\tinyweb1.h
103 | # End Source File
104 | # End Group
105 | # Begin Group "Resource Files"
106 |
107 | # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
108 | # End Group
109 | # End Target
110 | # End Project
111 |
--------------------------------------------------------------------------------
/win32/tinyweb2.dsp:
--------------------------------------------------------------------------------
1 | # Microsoft Developer Studio Project File - Name="tinyweb2" - Package Owner=<4>
2 | # Microsoft Developer Studio Generated Build File, Format Version 6.00
3 | # ** DO NOT EDIT **
4 |
5 | # TARGTYPE "Win32 (x86) Console Application" 0x0103
6 |
7 | CFG=tinyweb2 - Win32 Debug
8 | !MESSAGE This is not a valid makefile. To build this project using NMAKE,
9 | !MESSAGE use the Export Makefile command and run
10 | !MESSAGE
11 | !MESSAGE NMAKE /f "tinyweb2.mak".
12 | !MESSAGE
13 | !MESSAGE You can specify a configuration when running NMAKE
14 | !MESSAGE by defining the macro CFG on the command line. For example:
15 | !MESSAGE
16 | !MESSAGE NMAKE /f "tinyweb2.mak" CFG="tinyweb2 - Win32 Debug"
17 | !MESSAGE
18 | !MESSAGE Possible choices for configuration are:
19 | !MESSAGE
20 | !MESSAGE "tinyweb2 - Win32 Release" (based on "Win32 (x86) Console Application")
21 | !MESSAGE "tinyweb2 - Win32 Debug" (based on "Win32 (x86) Console Application")
22 | !MESSAGE
23 |
24 | # Begin Project
25 | # PROP AllowPerConfigDependencies 0
26 | # PROP Scc_ProjName ""
27 | # PROP Scc_LocalPath ""
28 | CPP=cl.exe
29 | RSC=rc.exe
30 |
31 | !IF "$(CFG)" == "tinyweb2 - Win32 Release"
32 |
33 | # PROP BASE Use_MFC 0
34 | # PROP BASE Use_Debug_Libraries 0
35 | # PROP BASE Output_Dir "Release"
36 | # PROP BASE Intermediate_Dir "Release"
37 | # PROP BASE Target_Dir ""
38 | # PROP Use_MFC 0
39 | # PROP Use_Debug_Libraries 0
40 | # PROP Output_Dir "Release"
41 | # PROP Intermediate_Dir "Release"
42 | # PROP Ignore_Export_Lib 0
43 | # PROP Target_Dir ""
44 | # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
45 | # ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
46 | # ADD BASE RSC /l 0x804 /d "NDEBUG"
47 | # ADD RSC /l 0x804 /d "NDEBUG"
48 | BSC32=bscmake.exe
49 | # ADD BASE BSC32 /nologo
50 | # ADD BSC32 /nologo
51 | LINK32=link.exe
52 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
53 | # ADD LINK32 libuv.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
54 |
55 | !ELSEIF "$(CFG)" == "tinyweb2 - Win32 Debug"
56 |
57 | # PROP BASE Use_MFC 0
58 | # PROP BASE Use_Debug_Libraries 1
59 | # PROP BASE Output_Dir "Debug"
60 | # PROP BASE Intermediate_Dir "Debug"
61 | # PROP BASE Target_Dir ""
62 | # PROP Use_MFC 0
63 | # PROP Use_Debug_Libraries 1
64 | # PROP Output_Dir "Debug"
65 | # PROP Intermediate_Dir "Debug"
66 | # PROP Ignore_Export_Lib 0
67 | # PROP Target_Dir ""
68 | # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
69 | # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
70 | # ADD BASE RSC /l 0x804 /d "_DEBUG"
71 | # ADD RSC /l 0x804 /d "_DEBUG"
72 | BSC32=bscmake.exe
73 | # ADD BASE BSC32 /nologo
74 | # ADD BSC32 /nologo
75 | LINK32=link.exe
76 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
77 | # ADD LINK32 libuvd.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
78 |
79 | !ENDIF
80 |
81 | # Begin Target
82 |
83 | # Name "tinyweb2 - Win32 Release"
84 | # Name "tinyweb2 - Win32 Debug"
85 | # Begin Group "Source Files"
86 |
87 | # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
88 | # Begin Source File
89 |
90 | SOURCE=..\test\main2.c
91 | # End Source File
92 | # Begin Source File
93 |
94 | SOURCE=..\membuf.c
95 | # End Source File
96 | # Begin Source File
97 |
98 | SOURCE=..\tinyweb2.c
99 | # End Source File
100 | # End Group
101 | # Begin Group "Header Files"
102 |
103 | # PROP Default_Filter "h;hpp;hxx;hm;inl"
104 | # Begin Source File
105 |
106 | SOURCE=..\membuf.h
107 | # End Source File
108 | # Begin Source File
109 |
110 | SOURCE=..\tinyweb2.h
111 | # End Source File
112 | # End Group
113 | # Begin Group "Resource Files"
114 |
115 | # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
116 | # End Group
117 | # End Target
118 | # End Project
119 |
--------------------------------------------------------------------------------
/win32/tinyweb3.dsp:
--------------------------------------------------------------------------------
1 | # Microsoft Developer Studio Project File - Name="tinyweb3" - Package Owner=<4>
2 | # Microsoft Developer Studio Generated Build File, Format Version 6.00
3 | # ** DO NOT EDIT **
4 |
5 | # TARGTYPE "Win32 (x86) Console Application" 0x0103
6 |
7 | CFG=tinyweb3 - Win32 Debug
8 | !MESSAGE This is not a valid makefile. To build this project using NMAKE,
9 | !MESSAGE use the Export Makefile command and run
10 | !MESSAGE
11 | !MESSAGE NMAKE /f "tinyweb3.mak".
12 | !MESSAGE
13 | !MESSAGE You can specify a configuration when running NMAKE
14 | !MESSAGE by defining the macro CFG on the command line. For example:
15 | !MESSAGE
16 | !MESSAGE NMAKE /f "tinyweb3.mak" CFG="tinyweb3 - Win32 Debug"
17 | !MESSAGE
18 | !MESSAGE Possible choices for configuration are:
19 | !MESSAGE
20 | !MESSAGE "tinyweb3 - Win32 Release" (based on "Win32 (x86) Console Application")
21 | !MESSAGE "tinyweb3 - Win32 Debug" (based on "Win32 (x86) Console Application")
22 | !MESSAGE
23 |
24 | # Begin Project
25 | # PROP AllowPerConfigDependencies 0
26 | # PROP Scc_ProjName ""
27 | # PROP Scc_LocalPath ""
28 | CPP=cl.exe
29 | RSC=rc.exe
30 |
31 | !IF "$(CFG)" == "tinyweb3 - Win32 Release"
32 |
33 | # PROP BASE Use_MFC 0
34 | # PROP BASE Use_Debug_Libraries 0
35 | # PROP BASE Output_Dir "Release"
36 | # PROP BASE Intermediate_Dir "Release"
37 | # PROP BASE Target_Dir ""
38 | # PROP Use_MFC 0
39 | # PROP Use_Debug_Libraries 0
40 | # PROP Output_Dir "Release"
41 | # PROP Intermediate_Dir "Release"
42 | # PROP Ignore_Export_Lib 0
43 | # PROP Target_Dir ""
44 | # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
45 | # ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /c
46 | # ADD BASE RSC /l 0x804 /d "NDEBUG"
47 | # ADD RSC /l 0x804 /d "NDEBUG"
48 | BSC32=bscmake.exe
49 | # ADD BASE BSC32 /nologo
50 | # ADD BSC32 /nologo
51 | LINK32=link.exe
52 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
53 | # ADD LINK32 libuv.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
54 |
55 | !ELSEIF "$(CFG)" == "tinyweb3 - Win32 Debug"
56 |
57 | # PROP BASE Use_MFC 0
58 | # PROP BASE Use_Debug_Libraries 1
59 | # PROP BASE Output_Dir "Debug"
60 | # PROP BASE Intermediate_Dir "Debug"
61 | # PROP BASE Target_Dir ""
62 | # PROP Use_MFC 0
63 | # PROP Use_Debug_Libraries 1
64 | # PROP Output_Dir "Debug"
65 | # PROP Intermediate_Dir "Debug"
66 | # PROP Ignore_Export_Lib 0
67 | # PROP Target_Dir ""
68 | # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
69 | # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
70 | # ADD BASE RSC /l 0x804 /d "_DEBUG"
71 | # ADD RSC /l 0x804 /d "_DEBUG"
72 | BSC32=bscmake.exe
73 | # ADD BASE BSC32 /nologo
74 | # ADD BSC32 /nologo
75 | LINK32=link.exe
76 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
77 | # ADD LINK32 libuvd.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
78 |
79 | !ENDIF
80 |
81 | # Begin Target
82 |
83 | # Name "tinyweb3 - Win32 Release"
84 | # Name "tinyweb3 - Win32 Debug"
85 | # Begin Group "Source Files"
86 |
87 | # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
88 | # Begin Source File
89 |
90 | SOURCE=..\test\main3.c
91 | # End Source File
92 | # Begin Source File
93 |
94 | SOURCE=..\membuf.c
95 | # End Source File
96 | # Begin Source File
97 |
98 | SOURCE=..\tinyweb3.c
99 | # End Source File
100 | # End Group
101 | # Begin Group "Header Files"
102 |
103 | # PROP Default_Filter "h;hpp;hxx;hm;inl"
104 | # Begin Source File
105 |
106 | SOURCE=..\membuf.h
107 | # End Source File
108 | # Begin Source File
109 |
110 | SOURCE=..\tinyweb3.h
111 | # End Source File
112 | # End Group
113 | # Begin Group "Resource Files"
114 |
115 | # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
116 | # End Group
117 | # End Target
118 | # End Project
119 |
--------------------------------------------------------------------------------
/tinyweb2.c:
--------------------------------------------------------------------------------
1 | #include "tinyweb2.h"
2 | #include "membuf.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | //Tinyweb v2, a tiny web server based on libuv, by liigo, 2013-6-24.
11 |
12 | uv_tcp_t _server;
13 | uv_tcp_t _client;
14 | uv_loop_t* _loop = NULL;
15 |
16 | static void tinyweb_on_connection(uv_stream_t* server, int status);
17 |
18 | //start web server, linstening ip:port
19 | //ip can be NULL or "", which means "0.0.0.0"
20 | void tinyweb_start(uv_loop_t* loop, const char* ip, int port) {
21 | struct sockaddr_in addr;
22 | uv_ip4_addr((ip && ip[0]) ? ip : "0.0.0.0", port, &addr);
23 | _loop = loop;
24 | uv_tcp_init(_loop, &_server);
25 | uv_tcp_bind(&_server, (const struct sockaddr*) &addr, 0);
26 | uv_listen((uv_stream_t*)&_server, 8, tinyweb_on_connection);
27 | }
28 |
29 | static void after_uv_close_client(uv_handle_t* client) {
30 | membuf_uninit((membuf_t*)client->data); //see: tinyweb_on_connection()
31 | free(client->data); //membuf_t*: request buffer
32 | free(client);
33 | }
34 |
35 | static void after_uv_write(uv_write_t* w, int status) {
36 | if(w->data)
37 | free(w->data); //copyed data
38 | uv_close((uv_handle_t*)w->handle, after_uv_close_client);
39 | free(w);
40 | }
41 |
42 | //close the connect of a client
43 | static void tinyweb_close_client(uv_stream_t* client) {
44 | uv_close((uv_handle_t*)client, after_uv_close_client);
45 | }
46 |
47 | //write data to client
48 | //data: the data to be sent
49 | //len: the size of data, can be -1 if data is string
50 | //need_copy_data: copy data or not
51 | //need_free_data: free data or not, ignore this arg if need_copy_data is non-zero
52 | //write_uv_data() will close client connection after writien success, because tinyweb use short connection.
53 | static void write_uv_data(uv_stream_t* client, const void* data, unsigned int len, int need_copy_data, int need_free_data) {
54 | uv_buf_t buf;
55 | uv_write_t* w;
56 | void* newdata = (void*)data;
57 |
58 | if(data == NULL || len == 0) return;
59 | if(len ==(unsigned int)-1)
60 | len = strlen((char*)data);
61 |
62 | if(need_copy_data) {
63 | newdata = malloc(len);
64 | memcpy(newdata, data, len);
65 | }
66 |
67 | buf = uv_buf_init((char*)newdata, len);
68 | w = (uv_write_t*)malloc(sizeof(uv_write_t));
69 | w->data = (need_copy_data || need_free_data) ? newdata : NULL;
70 | uv_write(w, client, &buf, 1, after_uv_write); //free w and w->data in after_uv_write()
71 | }
72 |
73 | //status: "200 OK"
74 | //content_type: "text/html"
75 | //content: any utf-8 data, need html-encode if content_type is "text/html"
76 | //content_length: can be -1 if content is string
77 | //returns malloc()ed respone, need free() by caller
78 | char* format_http_respone(const char* status, const char* content_type, const void* content, int content_length) {
79 | int totalsize;
80 | char* respone;
81 | if(content_length < 0)
82 | content_length = content ? strlen((char*)content) : 0;
83 | totalsize = strlen(status) + strlen(content_type) + content_length + 128;
84 | respone = (char*) malloc(totalsize);
85 | sprintf(respone, "HTTP/1.1 %s\r\n"
86 | "Content-Type:%s;charset=utf-8\r\n"
87 | "Content-Length:%d\r\n\r\n",
88 | status, content_type, content_length);
89 | if(content) {
90 | memcpy(respone + strlen(respone), content, content_length);
91 | }
92 | return respone;
93 | }
94 |
95 | #if defined(WIN32)
96 | #define snprintf _snprintf
97 | #endif
98 |
99 | //invoked by tinyweb when GET request comes in
100 | //please invoke write_uv_data() once and only once on every request, to send respone to client and close the connection.
101 | //if not handle this request (by invoking write_uv_data()), you can close connection using tinyweb_close_client(client).
102 | //pathinfo: "/" or "/book/view/1"
103 | //query_stirng: the string after '?' in url, such as "id=0&value=123", maybe NULL or ""
104 | static void tinyweb_on_request_get(uv_stream_t* client, const char* pathinfo, const char* query_stirng) {
105 | if(strcmp(pathinfo, "/") == 0) {
106 | char* respone = format_http_respone("200 OK", "text/html", "Welcome to tinyweb", -1);
107 | write_uv_data(client, respone, -1, 0, 1);
108 | } else if(strcmp(pathinfo, "/404") == 0) {
109 | char* respone = format_http_respone("404 Not Found", "text/html", "404 Page Not Found", -1);
110 | write_uv_data(client, respone, -1, 0, 1);
111 | } else {
112 | char* respone;
113 | char content[1024];
114 | snprintf(content, sizeof(content), "
pathinfo: %s
query stirng: %s
", pathinfo, query_stirng);
115 | respone = format_http_respone("200 OK", "text/html", content, -1);
116 | write_uv_data(client, respone, -1, 0, 1);
117 | }
118 | }
119 |
120 | static void on_uv_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
121 | *buf = uv_buf_init(malloc(suggested_size), suggested_size);
122 | }
123 |
124 | static void on_uv_read(uv_stream_t* client, ssize_t nread, const uv_buf_t* buf) {
125 | if(nread > 0) {
126 | char* crln2;
127 | membuf_t* membuf = (membuf_t*) client->data; //see tinyweb_on_connection()
128 | assert(membuf);
129 | membuf_append_data(membuf, buf->base, nread);
130 | if((crln2 = strstr((const char*)membuf->data, "\r\n\r\n")) != NULL) {
131 | const char* request_header = membuf->data;
132 | *crln2 = '\0';
133 | printf("\n----Tinyweb client request: ----\n%s\n", request_header);
134 | //handle GET request
135 | if(request_header[0]=='G' && request_header[1]=='E' && request_header[2]=='T') {
136 | char *query_stirng, *end;
137 | const char* pathinfo = request_header + 3;
138 | while(isspace(*pathinfo)) pathinfo++;
139 | end = strchr(pathinfo, ' ');
140 | if(end) *end = '\0';
141 | query_stirng = strchr(pathinfo, '?'); //split pathinfo and query_stirng using '?'
142 | if(query_stirng) {
143 | *query_stirng = '\0';
144 | query_stirng++;
145 | }
146 | tinyweb_on_request_get(client, pathinfo, query_stirng);
147 | //write_uv_data -> after_uv_write -> uv_close, will close the client, so additional processing is not needed.
148 | } else {
149 | tinyweb_close_client(client);
150 | }
151 | }
152 | } else if(nread == -1) {
153 | tinyweb_close_client(client);
154 | }
155 |
156 | if(buf->base)
157 | free(buf->base);
158 | }
159 |
160 | static void tinyweb_on_connection(uv_stream_t* server, int status) {
161 | assert(server == (uv_stream_t*)&_server);
162 | if(status == 0) {
163 | uv_tcp_t* client = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));
164 | client->data = malloc(sizeof(membuf_t));
165 | membuf_init((membuf_t*)client->data, 128);
166 | uv_tcp_init(_loop, client);
167 | uv_accept((uv_stream_t*)&_server, (uv_stream_t*)client);
168 | uv_read_start((uv_stream_t*)client, on_uv_alloc, on_uv_read);
169 |
170 | //write_uv_data((uv_stream_t*)client, http_respone, -1, 0);
171 | //close client after uv_write, and free it in after_uv_close()
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/tinyweb3.c:
--------------------------------------------------------------------------------
1 | #include "tinyweb3.h"
2 | #include "membuf.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | //Tinyweb v3, a tiny web server based on libuv, by liigo, 2013-6-30.
11 |
12 | uv_tcp_t _server;
13 | uv_tcp_t _client;
14 | uv_loop_t* _loop = NULL;
15 | const char* _doc_root_path = NULL;
16 |
17 | static void tinyweb_on_connection(uv_stream_t* server, int status);
18 |
19 | //start web server, linstening ip:port
20 | //ip can be NULL or "", which means "0.0.0.0"
21 | //doc_root_path can be NULL, or requires not end with /
22 | void tinyweb_start(uv_loop_t* loop, const char* ip, int port, const char* doc_root_path) {
23 | struct sockaddr_in addr;
24 | uv_ip4_addr((ip && ip[0]) ? ip : "0.0.0.0", port, &addr);
25 |
26 | _loop = loop;
27 | if(doc_root_path && doc_root_path[0])
28 | _doc_root_path = doc_root_path;
29 |
30 | uv_tcp_init(_loop, &_server);
31 | uv_tcp_bind(&_server, (const struct sockaddr*) &addr, 0);
32 | uv_listen((uv_stream_t*)&_server, 8, tinyweb_on_connection);
33 |
34 | printf("Tinyweb v3 is started, listening port %d...\n", port);
35 | printf("Please access http://localhost:%d from you web browser.\n", port);
36 | }
37 |
38 | static void after_uv_close_client(uv_handle_t* client) {
39 | membuf_uninit((membuf_t*)client->data); //see: tinyweb_on_connection()
40 | free(client->data); //membuf_t*: request buffer
41 | free(client);
42 | }
43 |
44 | static void after_uv_write(uv_write_t* w, int status) {
45 | if(w->data)
46 | free(w->data); //copyed data
47 | uv_close((uv_handle_t*)w->handle, after_uv_close_client);
48 | free(w);
49 | }
50 |
51 | //close the connect of a client
52 | static void tinyweb_close_client(uv_stream_t* client) {
53 | uv_close((uv_handle_t*)client, after_uv_close_client);
54 | }
55 |
56 | //write data to client
57 | //data: the data to be sent
58 | //len: the size of data, can be -1 if data is string
59 | //need_copy_data: copy data or not
60 | //need_free_data: free data or not, ignore this arg if need_copy_data is non-zero
61 | //write_uv_data() will close client connection after writien success, because tinyweb use short connection.
62 | static void write_uv_data(uv_stream_t* client, const void* data, unsigned int len, int need_copy_data, int need_free_data) {
63 | uv_buf_t buf;
64 | uv_write_t* w;
65 | void* newdata = (void*)data;
66 |
67 | if(data == NULL || len == 0) return;
68 | if(len ==(unsigned int)-1)
69 | len = strlen((char*)data);
70 |
71 | if(need_copy_data) {
72 | newdata = malloc(len);
73 | memcpy(newdata, data, len);
74 | }
75 |
76 | buf = uv_buf_init((char*)newdata, len);
77 | w = (uv_write_t*)malloc(sizeof(uv_write_t));
78 | w->data = (need_copy_data || need_free_data) ? newdata : NULL;
79 | uv_write(w, client, &buf, 1, after_uv_write); //free w and w->data in after_uv_write()
80 | }
81 |
82 | //status: "200 OK"
83 | //content_type: "text/html"
84 | //content: any utf-8 data, need html-encode if content_type is "text/html"
85 | //content_length: can be -1 if content is string
86 | //respone_size: if not NULL, the size of respone will be writen to it
87 | //returns malloc()ed respone, need free() by caller
88 | char* format_http_respone(const char* status, const char* content_type, const void* content, int content_length, int* respone_size) {
89 | int totalsize, header_size;
90 | char* respone;
91 | if(content_length < 0)
92 | content_length = content ? strlen((char*)content) : 0;
93 | totalsize = strlen(status) + strlen(content_type) + content_length + 128;
94 | respone = (char*) malloc(totalsize);
95 | header_size = sprintf(respone, "HTTP/1.1 %s\r\n"
96 | "Content-Type:%s;charset=utf-8\r\n"
97 | "Content-Length:%d\r\n\r\n",
98 | status, content_type, content_length);
99 | assert(header_size > 0);
100 | if(content) {
101 | memcpy(respone + header_size, content, content_length);
102 | }
103 | if(respone_size)
104 | *respone_size = header_size + content_length;
105 | return respone;
106 | }
107 |
108 | #if defined(WIN32)
109 | #define snprintf _snprintf
110 | #endif
111 |
112 | static void _404_not_found(uv_stream_t* client, const char* pathinfo) {
113 | char* respone;
114 | char buffer[1024];
115 | snprintf(buffer, sizeof(buffer), "404 Not Found
%s
", pathinfo);
116 | respone = format_http_respone("404 Not Found", "text/html", buffer, -1, NULL);
117 | write_uv_data(client, respone, -1, 0, 1);
118 | }
119 |
120 | //invoked by tinyweb when a file is request. see tinyweb_on_request_get()
121 | static void _on_send_file(uv_stream_t* client, const char* content_type, const char* file, const char* pathinfo) {
122 | int file_size, read_bytes, respone_size;
123 | unsigned char *file_data, *respone;
124 | FILE* fp = fopen(file, "rb");
125 | if(fp) {
126 | fseek(fp, 0, SEEK_END);
127 | file_size = ftell(fp);
128 | fseek(fp, 0, SEEK_SET);
129 | file_data = (unsigned char*) malloc(file_size);
130 | read_bytes = fread(file_data, 1, file_size, fp);
131 | assert(read_bytes == file_size);
132 | fclose(fp);
133 |
134 | respone_size = 0;
135 | respone = format_http_respone("200 OK", content_type, file_data, file_size, &respone_size);
136 | free(file_data);
137 | write_uv_data(client, respone, respone_size, 0, 1);
138 | } else {
139 | _404_not_found(client, pathinfo);
140 | }
141 | }
142 |
143 | //only identify familiar postfix currently
144 | static const char* _get_content_type(const char* postfix) {
145 | if(strcmp(postfix, "html") == 0 || strcmp(postfix, "htm") == 0)
146 | return "text/html";
147 | else if(strcmp(postfix, "js") == 0)
148 | return "text/javascript";
149 | else if(strcmp(postfix, "css") == 0)
150 | return "text/css";
151 | else if(strcmp(postfix, "jpeg") == 0 || strcmp(postfix, "jpg") == 0)
152 | return "image/jpeg";
153 | else if(strcmp(postfix, "png") == 0)
154 | return "image/png";
155 | else if(strcmp(postfix, "gif") == 0)
156 | return "image/gif";
157 | else if(strcmp(postfix, "txt") == 0)
158 | return "text/plain";
159 | else
160 | return "application/octet-stream";
161 | }
162 |
163 | //invoked by tinyweb when GET request comes in
164 | //please invoke write_uv_data() once and only once on every request, to send respone to client and close the connection.
165 | //if not handle this request (by invoking write_uv_data()), you can close connection using tinyweb_close_client(client).
166 | //pathinfo: "/" or "/book/view/1"
167 | //query_stirng: the string after '?' in url, such as "id=0&value=123", maybe NULL or ""
168 | static void tinyweb_on_request_get(uv_stream_t* client, const char* pathinfo, const char* query_stirng) {
169 | //request a file?
170 | char* postfix = strrchr(pathinfo, '.');
171 | if(postfix) {
172 | postfix++;
173 | if(_doc_root_path) {
174 | char file[1024];
175 | snprintf(file, sizeof(file), "%s%s", _doc_root_path, pathinfo);
176 | _on_send_file(client, _get_content_type(postfix), file, pathinfo);
177 | return;
178 | } else {
179 | _404_not_found(client, pathinfo);
180 | return;
181 | }
182 | }
183 |
184 | //request an action
185 | if(strcmp(pathinfo, "/") == 0) {
186 | char* respone = format_http_respone("200 OK", "text/html", "Welcome to tinyweb. index.html", -1, NULL);
187 | write_uv_data(client, respone, -1, 0, 1);
188 | } else if(strcmp(pathinfo, "/404") == 0) {
189 | char* respone = format_http_respone("404 Not Found", "text/html", "404 Page Not Found", -1, NULL);
190 | write_uv_data(client, respone, -1, 0, 1);
191 | } else {
192 | char* respone;
193 | char content[1024];
194 | snprintf(content, sizeof(content), "tinyweb
pathinfo: %s
query stirng: %s
", pathinfo, query_stirng);
195 | respone = format_http_respone("200 OK", "text/html", content, -1, NULL);
196 | write_uv_data(client, respone, -1, 0, 1);
197 | }
198 | }
199 |
200 | static void on_uv_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
201 | *buf = uv_buf_init(malloc(suggested_size), suggested_size);
202 | }
203 |
204 | static void on_uv_read(uv_stream_t* client, ssize_t nread, const uv_buf_t* buf) {
205 | if(nread > 0) {
206 | char* crln2;
207 | membuf_t* membuf = (membuf_t*) client->data; //see tinyweb_on_connection()
208 | assert(membuf);
209 | membuf_append_data(membuf, buf->base, nread);
210 | if((crln2 = strstr((const char*)membuf->data, "\r\n\r\n")) != NULL) {
211 | const char* request_header = membuf->data;
212 | *crln2 = '\0';
213 | printf("\n----Tinyweb client request: ----\n%s\n", request_header);
214 | //handle GET request
215 | if(request_header[0]=='G' && request_header[1]=='E' && request_header[2]=='T') {
216 | char *query_stirng, *end;
217 | const char* pathinfo = request_header + 3;
218 | while(isspace(*pathinfo)) pathinfo++;
219 | end = strchr(pathinfo, ' ');
220 | if(end) *end = '\0';
221 | query_stirng = strchr(pathinfo, '?'); //split pathinfo and query_stirng using '?'
222 | if(query_stirng) {
223 | *query_stirng = '\0';
224 | query_stirng++;
225 | }
226 | tinyweb_on_request_get(client, pathinfo, query_stirng);
227 | //write_uv_data -> after_uv_write -> uv_close, will close the client, so additional processing is not needed.
228 | } else {
229 | tinyweb_close_client(client);
230 | }
231 | }
232 | } else if(nread == -1) {
233 | tinyweb_close_client(client);
234 | }
235 |
236 | if(buf->base)
237 | free(buf->base);
238 | }
239 |
240 | static void tinyweb_on_connection(uv_stream_t* server, int status) {
241 | assert(server == (uv_stream_t*)&_server);
242 | if(status == 0) {
243 | uv_tcp_t* client = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));
244 | client->data = malloc(sizeof(membuf_t));
245 | membuf_init((membuf_t*)client->data, 128);
246 | uv_tcp_init(_loop, client);
247 | uv_accept((uv_stream_t*)&_server, (uv_stream_t*)client);
248 | uv_read_start((uv_stream_t*)client, on_uv_alloc, on_uv_read);
249 |
250 | //write_uv_data((uv_stream_t*)client, http_respone, -1, 0);
251 | //close client after uv_write, and free it in after_uv_close()
252 | }
253 | }
254 |
--------------------------------------------------------------------------------