Compare commits

...
442 Commits
Author SHA1 Message Date
Sergey Lyubka ace9285966 Updated years 2015-01-19 18:25:14 +00:00
Sergey Lyubka cdc167b0cc Merge pull request #459 from joshcriz/patch-1
Update Makefile
2015-01-19 16:32:24 +00:00
Josh Criz a0dd396b95 Update Makefile
Fixing typo in 'clean' section
2015-01-16 17:59:08 -06:00
Sergey Lyubka e4ad3010be Fix #416 2014-12-30 03:56:29 +00:00
Sergey Lyubka 03a82ef848 Merge pull request #452 from CurlyMoo/patch-2
Typo
2014-12-27 18:25:09 +00:00
CurlyMoo 9459d69dcc Typo 2014-12-27 18:35:09 +01:00
Sergey Lyubka b28662362d Merge pull request #449 from ursine/patch-1
64-bit length fields on ARM don't work
2014-12-21 20:54:52 +00:00
Sergey Lyubka 66450bf3d1 Merge pull request #450 from sebgod/master
mongoose.c: using application/javascript for .js
2014-12-21 16:07:12 +00:00
Sebastian Godelet 478ca05e7a mongoose.c: using application/javascript for .js
As of http://www.rfc-editor.org/rfc/rfc4329.txt, the preferred MIME type
for JavaScript is application/javascript.
2014-12-21 22:16:05 +08:00
Gary Coulbourne 8cad0a72f9 64-bit length fields on ARM don't work
ARM only allows aligned accesses.  GCC generated unaligned accesses for the copy buffer, but ARM forces alignment, causing the first two bytes to be trampled.  I changed the mechanism to create two temporaries and memcpy them in.  It now works on ARM (and x86) for large websocket chunks.
2014-12-17 19:14:30 -05:00
Sergey Lyubka 31178f4d48 Typo fixed in the comment 2014-12-15 12:03:50 +00:00
Sergey Lyubka 7a5485493e Added md5 copyright 2014-12-11 18:01:58 +00:00
Sergey Lyubka d3cbb160f5 Removed travis config file 2014-12-01 11:33:16 +00:00
Sergey Lyubka 45ff2c2c19 Removed $Date$ marker 2014-11-28 14:16:49 +00:00
Sergey Lyubka c3db7b141b Removed $Date$ from header 2014-11-28 12:21:36 +00:00
Sergey Lyubka 0a0ea36ace Fix examples/proxy_server build 2014-11-24 19:33:24 +00:00
Sergey Lyubka 72ed954721 Merge pull request #440 from zapline/patch-1
Update ReleaseNotes.md(spell mistake)
2014-11-20 07:35:24 +00:00
zapline 33e035273a Update ReleaseNotes.md
spell mistake
2014-11-20 09:58:29 +08:00
Sergey Lyubka 223f36c130 Updated Readme 2014-11-17 15:50:02 +00:00
Sergey Lyubka 4c0bc5bd03 Updated Readme 2014-11-17 15:49:37 +00:00
Sergey Lyubka 808a5f6f53 Update README.md 2014-11-13 13:48:43 +00:00
Sergey Lyubka 404e635665 Fixed Android build 2014-11-12 22:16:32 +00:00
Sergey Lyubka 9d7a60a63f Merge pull request #433 from jhnwkmn/malloc
Consistent use of NS_MALLOC, NS_FREE and NS_REALLOC
2014-11-10 10:35:07 +00:00
Johan Wikman ba9c74187b Consistent use of NS_MALLOC, NS_FREE and NS_REALLOC
malloc(), free() and realloc() were used in a number of places.
Added NS_CALLOC as well.
2014-11-10 08:29:19 +02:00
Sergey Lyubka e9d4847cbb Merge pull request #432 from jhnwkmn/o_binary
O_BINARY might be defined.
2014-11-09 10:06:16 +00:00
Sergey Lyubka ea76de07ee Merge pull request #436 from jhnwkmn/no-cgi
MONGOOSE_NO_CGI needs special handling.
2014-11-09 10:04:54 +00:00
Sergey Lyubka 5a8c468ff7 Merge pull request #435 from jhnwkmn/size-macros
Added size macros.
2014-11-09 10:01:59 +00:00
Sergey Lyubka 5b143a6237 Merge pull request #434 from jhnwkmn/no-user
Added MONGOOSE_NO_USER configuration define.
2014-11-09 09:56:31 +00:00
Sergey Lyubka d244a7fdfd Merge pull request #437 from jhnwkmn/nsf-close-immediately
If socket is about to be closed do not add to [read|write]_set.
2014-11-09 09:55:13 +00:00
Johan Wikman 300a27de99 If socket is about to be closed do not add to [read|write]_set.
If NSG_CLOSE_IMMEDIATELY is on, then the socket will be closed,
which means that (if the socket was added to either read_set or
write_set) the subsequent select will in turn be performed on a
socket that has been closed.

Standard socket implementations seem to ignore a descriptor
referring to a closed socket but LWIP
(http://savannah.nongnu.org/projects/lwip/) crashes. So better
not to add to the sets.
2014-11-07 15:48:23 +02:00
Johan Wikman 04a15a0c10 MONGOOSE_NO_CGI needs special handling.
MONGOOSE_NO_CGI is implied by both NS_DISABLE_SOCKETPAIR and
MONGOOSE_NO_FILESYSTEM. Thus, so as not to get complaints about
the same define being defined twice, some extra checks are needed.
2014-11-07 15:29:25 +02:00
Johan Wikman f0f6ae05aa Added size macros.
On very resource constraind devices you need to be able to
control the size of all buffers and structures placed on the
stack.
2014-11-07 14:31:44 +02:00
Johan Wikman 8a7d535d92 Added MONGOOSE_NO_USER configuration define.
Low-end systems, not running a proper OS, may not have the
concept of a user.
2014-11-07 14:01:13 +02:00
Sergey Lyubka e7ac6024de Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-11-07 10:05:51 +00:00
Sergey Lyubka 9981b99ac8 Extra NULL check in iter2(). Fix #430 2014-11-07 10:05:42 +00:00
Johan Wikman 87a36c0c30 O_BINARY might be defined.
Some unix environments defines O_BINARY.
2014-11-07 12:02:11 +02:00
Sergey Lyubka f2b13761c0 Merge pull request #431 from jhnwkmn/reduce-includes
Conditionally include dirent.h and dlfcn.h
2014-11-07 09:51:30 +00:00
Johan Wikman 9786478a31 Conditionally include dirent.h and dlfcn.h
If you do not have a filesystem or do not support dav or
dynamic loading, the corresponding header files need not
be included (some environments might not have them).
2014-11-07 11:05:51 +02:00
Sergey Lyubka 7d236d7f33 Merge pull request #429 from AlexAltea/master
Added #define's for deprecated POSIX names
2014-11-01 20:39:34 +00:00
Alexandro Sánchez Bach e098a7292d Added #define's for deprecated POSIX names 2014-10-30 23:07:48 +01:00
Sergey Lyubka 632b3a38a9 5.5 -> 5.6 2014-10-28 11:43:51 +00:00
Sergey Lyubka 23141c2fef Added 5.5 release notes 2014-10-28 11:42:47 +00:00
Sergey Lyubka c8ba652f1c 5.4 -> 5.5 2014-10-28 11:20:44 +00:00
Sergey Lyubka 6ac2fa2ae6 Updated listening_port for SSL option 2014-10-28 11:20:10 +00:00
Sergey Lyubka 0322ec5647 Include query_string when proxying requests 2014-10-22 12:14:21 +01:00
Sergey Lyubka a5223a9850 Squashed ARM warnings 2014-10-21 15:51:42 +01:00
Sergey Lyubka 016968ada1 Removed net_skeleton dependency from examples/proxy_server 2014-10-17 20:29:39 +01:00
Sergey Lyubka 710b888122 Added -pthread to test/Makefile::CFLAGS 2014-10-17 20:12:29 +01:00
Sergey Lyubka cf53959f34 Fix broken push (type cast) 2014-10-17 20:03:15 +01:00
Sergey Lyubka 30d29fb77f Merge pull request #425 from mpromonet/master
make compile with g++ (fix invalid cast from ‘void*’)
2014-10-17 19:49:59 +01:00
MPR c05b51bbfc make compile with g++ (fix invalid conversion from ‘void*’ to ‘ns_connection*’) 2014-10-16 23:57:14 +02:00
Sergey Lyubka 1ab704ac92 Fixed -DMONGOOSE_NO_DIRECTORY_LISTING build 2014-10-16 10:46:33 +01:00
Sergey Lyubka 2bc77e065c Fixed build 2014-10-15 14:29:22 +01:00
Sergey Lyubka 790daabf7d Enhanced RESTful API example 2014-10-15 11:58:47 +01:00
Sergey Lyubka a6598a1278 Added #define close(x) _close(x) for WIN32 to squash warning 4996 that VS2013 gives 2014-10-15 08:30:49 +01:00
Sergey Lyubka 0af5502b74 Removed redundant #defines for -D_WIN32 case 2014-10-15 08:28:17 +01:00
Sergey Lyubka f059e41cac Removed mg_set_listening_socket() and mg_get_listening_socket(), added mg_copy_listeners() 2014-10-14 15:25:56 +01:00
Sergey Lyubka 2cce91de9a Refactored code for multiple listeners 2014-10-14 14:22:26 +01:00
Sergey Lyubka 0ff5b5b044 Added support for multiple listeners 2014-10-14 13:59:22 +01:00
Sergey Lyubka 0c5d9a98c2 Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-10-14 11:10:47 +01:00
Sergey Lyubka c9fbdfc1b5 mg_open() accepts 3rd param: mode 2014-10-14 11:10:42 +01:00
Sergey Lyubka 6193b5171c Merge pull request #421 from mpromonet/master
transmit http answer code using http_client
2014-10-14 11:10:03 +01:00
Sergey Lyubka 911115ccc0 Added -pthread to CFLAGS for linux 2014-10-14 10:05:12 +00:00
Sergey Lyubka 2f3ddeccc5 Properly trim whitespaces from the hashbang CGI interpreter path 2014-10-13 15:57:34 +01:00
MPR ca3f5a3257 follow Google style 2014-10-12 13:58:06 +02:00
MPR df206bc05a transmit http answer code using http_client 2014-10-11 17:56:35 +02:00
Sergey Lyubka 531fec34fd Added missing files 2014-10-10 14:35:39 +01:00
Sergey Lyubka 4c10d1f356 mongoose_server -> web_server 2014-10-10 13:58:45 +01:00
Sergey Lyubka 94dccdd6f4 c# -> csharp 2014-10-10 10:02:58 +01:00
Sergey Lyubka 544352b7d3 moved jni to android_build 2014-10-10 09:51:57 +01:00
Sergey Lyubka 29c2ae4886 moved multi_threaded to separate dir 2014-10-10 09:51:02 +01:00
Sergey Lyubka 1a2e2ec2fd moved proxy to separate dir 2014-10-10 09:48:43 +01:00
Sergey Lyubka 3e780d21fc moved form.c example to separate dir 2014-10-10 09:44:45 +01:00
Sergey Lyubka 63fcea0159 moved form_submit to separate dir 2014-10-10 09:40:07 +01:00
Sergey Lyubka e5d21225be moved big_upload to separate dir 2014-10-10 09:39:12 +01:00
Sergey Lyubka 0c9caf3120 Moved server.c to separate dir 2014-10-10 09:37:18 +01:00
Sergey Lyubka 54ba36c1c9 check_login_form_submission() factored in separate function 2014-10-09 11:44:12 +01:00
Sergey Lyubka 5077350589 Added cookie based auth example 2014-10-09 11:29:39 +01:00
Sergey Lyubka 2d880c4f5c Moved auth.c to separate dir 2014-10-09 09:42:07 +01:00
Sergey Lyubka 4ca4c862a5 Updated net_skeleton, added extra_headers param to mg_send_file() 2014-10-09 09:26:45 +01:00
Sergey Lyubka 0918df1566 Removed reference to chat.c 2014-10-05 17:29:33 +01:00
Sergey Lyubka aa2583cc26 Added enable_proxy 2014-10-03 11:54:00 +01:00
Sergey Lyubka e1b1d39635 Removed conflicting MIT-related comment block 2014-10-01 12:11:04 +01:00
Sergey Lyubka 3bf82dd541 Merge pull request #419 from grodansparadis/master
connection_data -> user_data
2014-09-22 15:03:19 +01:00
Sergey Lyubka 2896965c97 Fixed link to examples 2014-09-20 08:32:29 +01:00
Ake Hedman 53b784b143 connection_data -> user_data 2014-09-19 08:20:37 +02:00
Sergey Lyubka 8e91e3a305 Timeout only non-listening connections 2014-09-16 07:47:40 +01:00
Sergey Lyubka dbc07c8b00 Moved mjpg_streamer to a separate dir 2014-09-15 19:05:30 +01:00
Sergey Lyubka ce953575e2 Not closing listening connections 2014-09-15 13:48:55 +01:00
Sergey Lyubka 80db048578 Added more dependencies in test/Makefile 2014-09-15 01:28:03 +01:00
Sergey Lyubka 382d165574 Added MONGOOSE_NO_MMAP conditional 2014-09-15 01:18:38 +01:00
Sergey Lyubka 0fb2f38cb7 Fixed mg_connect() call 2014-09-14 15:06:09 +01:00
Sergey Lyubka aa007f1285 Updated travis config 2014-09-11 15:55:18 +01:00
Sergey Lyubka 8752910330 Better UDP support 2014-09-11 15:52:42 +01:00
Sergey Lyubka 2a5a8eec49 removed chat_server dir in examples 2014-09-10 00:59:31 +01:00
Sergey Lyubka ceca52de30 Moved file send_file example to separate dir 2014-09-09 23:23:29 +01:00
Sergey Lyubka 8a3691055c Moved file upload example to separate dir 2014-09-09 23:20:23 +01:00
Sergey Lyubka 56d12c89da Typo in hello_world.c fixed 2014-09-09 22:47:07 +01:00
Sergey Lyubka 2cfce4d8b2 Added copyright 2014-09-09 22:46:24 +01:00
Sergey Lyubka f372faf614 Moved hello.c example to its own dir 2014-09-09 22:44:57 +01:00
Sergey Lyubka 882f3b3ac3 removed hello.lp 2014-09-09 18:26:03 +01:00
Sergey Lyubka fac0e9b0ac Moved unit test to dedicated dir 2014-09-09 18:23:48 +01:00
Sergey Lyubka a3cf67c380 Moved examples/mkdata.pl -> scripts/embed_binary_files.pl 2014-09-09 18:11:57 +01:00
Sergey Lyubka 2987cbdb5c ws_ssl -> websocket_ssl_proxy 2014-09-09 18:09:33 +01:00
Sergey Lyubka 6083b9c5c5 Updated to the recent skeleton. SSL address format changed 2014-09-09 18:07:55 +01:00
Sergey Lyubka 83237a02d9 Made local copies of dependencies 2014-09-09 17:03:50 +01:00
Sergey Lyubka 965188bac9 removed chat.c 2014-09-09 17:01:06 +01:00
Sergey Lyubka 66da41d401 Adopted to the recent net_skeleton 2014-09-09 17:00:49 +01:00
Sergey Lyubka cfd334ac6c Moved websocket echo server example 2014-09-09 09:27:35 +01:00
Sergey Lyubka b5f6254c1e Added date marker 2014-09-08 23:30:52 +01:00
Sergey Lyubka dcbd1cbbd1 Moved pubsub example to websocket_chat 2014-09-08 23:29:34 +01:00
Sergey Lyubka a3b568f460 removed lua_ddl.c 2014-09-08 23:22:46 +01:00
Sergey Lyubka 75770aec80 Merge pull request #415 from mmicko/master
Added mg_mmap and mg_munmap utility functions for memory mapping files
2014-09-07 11:55:27 +01:00
Sergey Lyubka 3a8ae7ac17 Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-09-06 10:11:43 +01:00
Sergey Lyubka b116b2fa82 mg_poll_server doc corrected 2014-09-06 10:10:22 +01:00
m.milanovic@levi9.com 7806609773 Added mg_mmap and mg_munmap utility functions for memory mapping files 2014-09-04 15:05:42 +02:00
Sergey Lyubka 72f45aa7e2 Merge pull request #414 from mmicko/master
Fixed compile on varois MINGW versions
2014-09-02 12:34:58 +01:00
m.milanovic@levi9.com 5e3e6a6dce Fixed compile on MINGW so it is confirmed working on versions 4.6.x till 4.9.x 2014-09-02 12:11:21 +02:00
Sergey Lyubka 1388bb1db4 Fixed line endings 2014-09-01 21:02:16 +01:00
Sergey Lyubka 3cc0d16f65 Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-09-01 20:55:37 +01:00
Sergey Lyubka b50095fa61 Added $Date$ markers 2014-09-01 20:54:12 +01:00
Sergey Lyubka 7ad85219d8 Merge pull request #413 from mcallegari/master
Fixed build warning of signed/unsigned conditional expression
2014-08-30 12:42:09 +01:00
Massimo Callegari 347a249d10 Fixed build warning of signed/unsigned conditional expression 2014-08-30 13:04:11 +02:00
Sergey Lyubka 07d982129a Passing NS events together with the NC pointer 2014-08-29 23:28:34 +01:00
Sergey Lyubka 73f2602ba0 Updated due to ssl_wrapper.h refactoring 2014-08-26 14:42:24 +01:00
Sergey Lyubka 44561d163a updated travis config 2014-08-22 22:39:48 +01:00
Sergey Lyubka c941a1a39a Proper struct mg_connection cleanup in close_local_endpoint() 2014-08-22 16:26:30 +01:00
Sergey Lyubka bee22312bc removed dead code 2014-08-21 04:13:33 +01:00
Sergey Lyubka bdb16c624c Added MG_RECV event and mg_send_file_data() func 2014-08-21 04:09:06 +01:00
Sergey Lyubka e389e70d05 squashed warn in mg_terminate_ssl() 2014-08-20 21:54:09 +01:00
Sergey Lyubka 695559f691 Updated to the latest SSL wrapper 2014-08-13 09:17:20 +01:00
Sergey Lyubka 13be74b4c6 Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-08-11 16:20:25 +01:00
Sergey Lyubka 02a1032fd4 updated ssl_wrapper 2014-08-11 16:19:25 +01:00
Sergey Lyubka 788f35b19c Merge pull request #411 from mmicko/master
Make mongoose compile on mingw environment
2014-08-11 15:01:33 +01:00
Miodrag Milanovic 3721efe1e9 Make mongoose compile on mingw environment 2014-08-11 15:27:23 +02:00
Sergey Lyubka e0822ffb78 Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-08-08 14:25:58 +01:00
Sergey Lyubka ae7d5a8f94 Bumping up default timeout from 30 to 300 sec 2014-08-08 14:25:44 +01:00
Sergey Lyubka ced7a2e0b5 Merge pull request #410 from zinosat/avoid_largefile_redefinition
Avoid unconditional definition of _LARGEFILE_SOURCE
2014-08-07 22:13:09 +01:00
Davide Viti 5ed6708468 Avoid unconditional definition of _LARGEFILE_SOURCE
On some build systems (i.e. Buildroot), _LARGEFILE_SOURCE is defined
at "higher level" as default CFLAG definition.
This fix avoids the following compilation warning:

mongoose-5.4/mongoose.c:52:0: warning: "_LARGEFILE_SOURCE" redefined [enabled by default]
 #define _LARGEFILE_SOURCE       // Enable fseeko() and ftello() functions
 ^
<command-line>:0:0: note: this is the location of the previous definition
2014-08-07 22:08:12 +02:00
Sergey Lyubka 9fe53f307b Added http client example 2014-08-05 17:14:41 +01:00
Sergey Lyubka c64d30e1f6 removed make_settings.mak 2014-08-05 16:41:14 +01:00
Sergey Lyubka b4c25fc7ba Added ALL_PROGS 2014-08-05 16:40:51 +01:00
Sergey Lyubka b67b618811 Added guard for MONGOOSE_NO_FILESYSTEM 2014-08-04 22:32:24 +01:00
Sergey Lyubka 5a19438f70 Added guard for MONGOOSE_NO_FILESYSTEM 2014-08-04 22:31:59 +01:00
Sergey Lyubka 860addf1e3 MONGOOSE_NO_SOCKETPAIR -> NS_DISABLE_SOCKETPAIR 2014-08-03 18:56:19 +01:00
Sergey Lyubka 03d571a119 Merge pull request #407 from kuiash/master
Populate HTTP warning codes
2014-08-03 18:29:15 +01:00
Sergey Lyubka 0d9ecffc53 Do not dealloc conn->request in mg_forward() 2014-08-03 12:41:15 +01:00
Daniel O'Connell d4c8384b85 ws_ssl example 2014-08-03 12:40:10 +01:00
Sergey Lyubka bd40145301 moved hex dumping to skeleton 2014-08-01 17:35:59 +01:00
Sergey Lyubka 9b42494316 Added general example makefile settings 2014-08-01 13:30:05 +01:00
Sergey Lyubka 2daff6959f Added #define matrix 2014-08-01 09:11:00 +01:00
Sergey Lyubka e9c434a6a1 updated net skeleton 2014-08-01 09:05:50 +01:00
Sergey Lyubka d6fba7e45d Added travis file 2014-08-01 08:08:54 +01:00
Sergey Lyubka 0a0130b90c removed 2014-08-01 07:53:51 +01:00
Sergey Lyubka facf2af66e Clearing off HTTP headers in close_local_endpoint() 2014-07-31 20:05:25 +01:00
Sergey Lyubka cc54195f59 Added mg_forward() 2014-07-31 20:04:59 +01:00
Sergey Lyubka e2fb707d9f Added SSL_CTX_set_mode() 2014-07-30 11:27:37 +01:00
kuiash 7dfff935d6 Populate HTTP warning codes
Includes changing 201 "Server Error" to "Internal Server Error"
List mostly borrowed from here http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
418 and 451 also added
Local testing showed a problem with 202 reported as "Server Error" instead of "Accepted"
2014-07-29 12:48:22 +01:00
Sergey Lyubka 5727a5c0ed Removed lua examples 2014-07-29 07:39:20 +01:00
Sergey Lyubka 268d2e9cbe better explanation for websocket2.c example 2014-07-28 12:19:00 +01:00
Sergey Lyubka 1e7da874a4 Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-07-28 12:15:23 +01:00
Sergey Lyubka 7875a8c2bd release notes for 5.4 2014-07-28 12:15:13 +01:00
Sergey Lyubka b0355d45ae Merge pull request #403 from pavel-pimenov/fix-cache-call-stat
stat - function result cache
2014-07-27 11:13:26 +01:00
Sergey Lyubka 91136d7731 Merge pull request #406 from Cykey/master
Set the correct json content-type
2014-07-27 11:12:52 +01:00
Pavel Pimenov 01a058d29b Remove file_stat_t st_cgi; 2014-07-27 13:10:49 +04:00
Pavel Pimenov e6eca1738f 80 characters limit + remove tabs 2014-07-27 12:53:37 +04:00
Pavel Pimenov 2108ec5a0c Fix style for pointer 2014-07-27 12:44:08 +04:00
David M. bc4e47630a Set the correct json content-type
Chance text/json to application/json to comply with the RFC.

See http://www.ietf.org/rfc/rfc4627.txt for more information.
2014-07-26 20:55:59 -04:00
Pavel Pimenov f41c9a2a1f stat - function result cache 2014-07-23 11:26:20 +04:00
Sergey Lyubka 6c77208236 release notes dates 2014-07-21 21:38:45 +01:00
Sergey Lyubka 386d2e5cd4 fix leak in handle_propfind() 2014-07-18 13:00:43 +01:00
Sergey Lyubka b738fdbffa updated net skeleton 2014-07-08 11:28:03 +01:00
Sergey Lyubka b5704850db Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-07-08 11:10:17 +01:00
Sergey Lyubka 9c67e8960e updated certs 2014-07-08 11:10:08 +01:00
Sergey Lyubka d3dff4e1d5 Merge pull request #391 from pavel-pimenov/pvs-studio-fix-zu
Change printf mask %zu -> %lu
2014-07-07 08:58:23 +01:00
Pavel Pimenov 82872e86fd Change printf mask %zu -> %lu
for MS VC++ compatibility http://stackoverflow.com/questions/15610053/correct-printf-format-specifier-for-size-t-zu-or-iu
2014-07-07 09:24:48 +04:00
Sergey Lyubka ac229b6606 Merge pull request #387 from Z-Wave-Me/master
Disallow WebDAV MKCOL/PUT/DELETE requests to protected files
2014-06-28 21:40:11 +01:00
Alex Skalozub a6bf2bcbf4 Should also disallow PROPFIND as well 2014-06-29 00:03:52 +04:00
Alex Skalozub b0cf8304cb Disallow WebDAV MKCOL/PUT/DELETE requests to protected files (like .htpasswd) 2014-06-28 23:14:21 +04:00
Sergey Lyubka 0d9fe39dbb ns_read_from_socket(): calling recv() in a loop until failure 2014-06-25 11:43:07 +01:00
Sergey Lyubka 708af6d9c5 more comments in big_upload.c 2014-06-24 17:09:00 +01:00
Sergey Lyubka e4c71425c9 Added big_upload.c 2014-06-24 17:04:26 +01:00
Sergey Lyubka 6e07215c1c Using Multiple inputs for upload example 2014-06-24 16:35:59 +01:00
Sergey Lyubka ca9603301e removed trailing newline 2014-06-24 16:35:26 +01:00
Sergey Lyubka d6c388042c QNX SSL upload fix 2014-06-23 13:52:07 +01:00
Sergey Lyubka 6f66d794e8 Refactor NS_POLL case in mg_ev_handler 2014-06-20 11:41:13 +01:00
Sergey Lyubka 1389eec784 Added comment for mg_terminate_ssl() call 2014-06-19 09:55:10 +01:00
Sergey Lyubka 6ef795e082 manual mitm for oracle.com 2014-06-19 09:53:38 +01:00
Sergey Lyubka 1e5d258e9e Wording for licensing clarified 2014-06-18 11:30:57 +01:00
Sergey Lyubka 70eb4c4bbc exported mg_terminate_ssl 2014-06-18 10:59:29 +01:00
Sergey Lyubka a639e10a4d squash QNX warning about execle() call 2014-06-17 13:28:45 +01:00
Sergey Lyubka 48750e67f6 ENABLE_PROXY is visible with MONGOOSE_NO_FILESYSTEM 2014-06-17 10:50:47 +01:00
Sergey Lyubka ce60379af6 sending 405 for proxy request if connect is not allowed 2014-06-16 11:27:17 +01:00
Sergey Lyubka e0b9e4abfd Added enable_proxy 2014-06-16 11:06:57 +01:00
Sergey Lyubka 665f205527 squash warning under mingw 2014-06-16 10:46:50 +01:00
Sergey Lyubka 030daaf5d5 squash warning under mingw 2014-06-16 10:44:56 +01:00
Sergey Lyubka 760511ec49 squashed warning around NS_STACK_SIZE 2014-06-16 10:21:31 +01:00
Sergey Lyubka 203688a34b Added comment for Win32 SO_RESUSEADDR 2014-06-13 10:19:46 +01:00
Sergey Lyubka 29f4703e4f Adding zero-length chunk for long-running requests 2014-06-13 07:57:27 +01:00
Sergey Lyubka 3cec6f6745 keeping alive long-lived connections 2014-06-12 10:58:49 +01:00
Sergey Lyubka abcea07641 Fix #373 2014-06-11 20:37:35 +01:00
Sergey Lyubka 6a54704b29 mg_set_option(): always set correct actual listening port 2014-06-11 20:12:23 +01:00
Sergey Lyubka 8a2fbac4c0 Calling event handler in MITM mode before making connection to the target host 2014-06-11 09:56:34 +01:00
Sergey Lyubka 96ce6bac69 ns_server_poll(): setting current_time to now after select() 2014-06-11 09:44:18 +01:00
Sergey Lyubka 50c0b958ff Added -root cmd line flag to proxy example 2014-06-10 12:22:50 +01:00
Sergey Lyubka 44416902d6 Changed debug in do_proxy() 2014-06-10 12:22:30 +01:00
Sergey Lyubka 25e646c1a1 Squashed warnings for MONGOOSE_NO_FILESYSTEM 2014-06-10 10:26:58 +01:00
Sergey Lyubka bd723bdc38 Removed dead code in websocket2.c 2014-06-09 17:33:26 +01:00
Sergey Lyubka 5e57031724 Added pubsub example 2014-06-09 17:26:22 +01:00
Sergey Lyubka c583913290 Added pubsub example 2014-06-09 17:25:08 +01:00
Sergey Lyubka 870ca615de Added mg_next() 2014-06-09 17:24:51 +01:00
Sergey Lyubka 64b51699f8 Added MG_WS_CONNECT 2014-06-09 16:32:56 +01:00
Sergey Lyubka 50a889dac0 Inlined parse_url() 2014-06-04 12:32:47 +01:00
Sergey Lyubka 0ff0788458 Formatting change for mg_send_file() 2014-06-04 12:32:31 +01:00
Sergey Lyubka fec6009edb mg_send_file() doc changed 2014-06-03 18:49:17 +01:00
Sergey Lyubka c4f172a120 Documented mg_send_file() 2014-06-03 18:45:37 +01:00
Sergey Lyubka 464c77314d Amended file sending example 2014-06-03 18:03:13 +01:00
Sergey Lyubka f51ca8d5d1 Added mg_send_file(). Changed sending function to return send_iobuf length. 2014-06-03 18:02:55 +01:00
Sergey Lyubka 7b37e0cb8b Added simplistic flow control in transfer_file_data() 2014-06-03 17:00:52 +01:00
Sergey Lyubka c7925353e7 Added parens around expression to squash warning under QNX SDP 6.6.0 2014-06-03 14:25:54 +01:00
Sergey Lyubka bd23ec4323 Removed a bit of dead code. Added more comments in proxy_event_handler() 2014-05-29 17:40:06 +01:00
Daniel O'Connell 3671b2632d proxy unit test reference implementation 2014-05-29 17:04:28 +01:00
Daniel O'Connell 25115a694c proxy support changes 2014-05-29 17:04:13 +01:00
Sergey Lyubka 2436f9d6c3 squashed win32 warnings 2014-05-22 07:31:19 +01:00
Sergey Lyubka 2238951c18 Integrated Dans net skeleton changes 2014-05-21 16:18:21 +01:00
Daniel O'Connell f266dd2c94 Removed noisy debug messages 2014-05-20 16:40:47 +01:00
Daniel O'Connell b03358cb03 Fixes for the proxy code 2014-05-19 19:08:02 +01:00
Sergey Lyubka 1bede7e28b Added test for parse_url() 2014-05-19 12:56:07 +01:00
Sergey Lyubka 3536670b82 re-worded FAQ section 2014-05-18 16:13:04 +01:00
Daniel O'Connell 3235c05fd6 Added proxy functionality 2014-05-18 13:22:33 +01:00
Daniel O'Connell 63db54e7b5 Added AV faq 2014-05-18 10:53:54 +01:00
Daniel O'Connell 1db51eb788 Moved sse.html to proxy_web_root/index.html 2014-05-13 14:51:16 +01:00
Daniel O'Connell 0e640ae885 Made setopt() wrapper around mg_set_option() 2014-05-13 13:02:08 +01:00
Daniel O'Connell 1bff6df2a7 Enabled SSL for proxy example 2014-05-13 12:33:27 +01:00
Daniel O'Connell d0e66de225 Re-added ssl_ca_certificate option 2014-05-13 12:30:30 +01:00
Daniel O'Connell f537670ce2 Synced net skeleton 2014-05-13 12:28:39 +01:00
Daniel O'Connell 4cb3341a15 Added proxy example, with SSE html page 2014-05-13 11:22:31 +01:00
Daniel O'Connell d52d9a4a6f Added pem files 2014-05-13 11:08:18 +01:00
Daniel O'Connell 4a076c7ea4 Added proxy to the clean target 2014-05-13 10:56:32 +01:00
Sergey Lyubka db6e883c22 Added mg_wakeup_server_ex(), removed mg_wakeup_server() and mg_iterate_over_connections() 2014-05-13 09:33:10 +01:00
Sergey Lyubka 804da054b5 Removed extra line in the comment 2014-05-12 23:24:03 +01:00
Sergey Lyubka fb4e701545 Added mg_wakeup_server_ex() for pushing data 2014-05-12 23:22:57 +01:00
Sergey Lyubka ccff4abf13 Added proxy target 2014-05-12 10:47:08 +01:00
Sergey Lyubka 24a7936907 Squashed warning for non-ssl case in ns_set_ssl_ca_cert() 2014-05-11 16:05:07 +01:00
Sergey Lyubka 0993994139 Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-05-11 13:43:10 +01:00
Sergey Lyubka 8b740536de Added ssl_ca_certificate option 2014-05-11 13:42:59 +01:00
Sergey Lyubka e851aac853 Merge pull request #371 from cry-inc/master
Fixed wrong behavior of mg_set_option() with option "listening_port" caused by SO_REUSEADDR when using Windows
2014-05-07 17:00:36 +01:00
cry-inc a39861b7bf Fixed wrong behavior of mg_set_option() with option "listening_port" caused by SO_REUSEADDR when using Windows 2014-05-07 16:38:23 +02:00
Sergey Lyubka 63490249ba Merge pull request #368 from cry-inc/master
Fixed wrong MIME type for .rar files
2014-04-30 10:45:09 +01:00
cry-inc 704f057384 Fixed wrong MIME type for .rar files 2014-04-30 10:30:35 +02:00
Sergey Lyubka 40968bc496 Moved other products in a different section 2014-04-30 09:18:55 +01:00
Sergey Lyubka 6873f3c5f6 Removed idle_timeout_ms 2014-04-29 18:01:25 +01:00
Sergey Lyubka ba5cd7267a Added MONGOOSE_SEND_NS_EVENTS to the embedding doc 2014-04-28 15:04:39 +01:00
Sergey Lyubka 1bb98e5e8e Exported mg_websocket_printf(), mg_url_encode(). Added unit test for mg_url_encode() 2014-04-25 18:04:24 +01:00
Sergey Lyubka a7774d0f1a Added mg_websocket_printf(). added MONGOOSE_SEND_NS_EVENTS 2014-04-24 14:09:54 +01:00
Sergey Lyubka f52c756fbb Calling SSL_free() in ns_close_conn() 2014-04-16 16:45:31 +01:00
Sergey Lyubka cc586c6ba2 php->basic 2014-04-15 14:13:38 +01:00
Sergey Lyubka 606eab7f4f Passing PUT content to the event handler 2014-04-14 16:14:13 +01:00
Sergey Lyubka 872b85b5f2 more on -access_control_list 2014-04-14 13:03:47 +01:00
Sergey Lyubka 35bd2b6032 Added form.c 2014-04-10 22:18:01 +01:00
Sergey Lyubka c5287b9044 iobuf_append() refactored: allocating exactly as many bytes as needed 2014-04-09 23:52:16 +01:00
Sergey Lyubka 223f6d960e README message changed 2014-04-09 18:10:49 +01:00
Sergey Lyubka 464b895116 Added enum for websocket opcodes 2014-04-09 15:16:03 +01:00
Sergey Lyubka 05d7b7c8bc Added MG_WS_HANDSHAKE 2014-04-09 11:55:36 +01:00
Sergey Lyubka d8b4db37a0 Squashed warnings 2014-04-09 11:37:08 +01:00
Sergey Lyubka 6bb49c780c Updated net skeleton code 2014-04-09 11:32:23 +01:00
Sergey Lyubka 3a60c5764d Unit test target corrected 2014-04-07 19:07:41 +01:00
Sergey Lyubka a8b38e4ab9 mg_set_option() sets NULL if value is empty 2014-03-31 15:02:20 +01:00
Sergey Lyubka 8750f8af1f Added cast in try_parse() 2014-03-31 09:51:47 +01:00
Sergey Lyubka e94a02dc12 Added more in 5.4 release notes 2014-03-31 09:49:42 +01:00
Sergey Lyubka 5680b0fcc5 Removed redundant cast in try_parse() 2014-03-31 09:34:31 +01:00
Sergey Lyubka 33c56b0edc Init local_ip and local_port properly in mg_connection 2014-03-30 22:50:10 +01:00
Sergey Lyubka e62e48397d Keeping absolute path for config file 2014-03-30 16:00:03 +01:00
Sergey Lyubka 8be30350be mg_set_option(): allow NULL as value. Fast success if old_value == new_value. 2014-03-30 15:59:39 +01:00
Sergey Lyubka 2609a2ab72 Fixed comment for mg_template() 2014-03-30 12:47:27 +01:00
Sergey Lyubka 5a04ab2c56 Added mg_template() function 2014-03-30 12:45:48 +01:00
Sergey Lyubka 4739de6c28 Removed DLL interface and Lua conditional 2014-03-30 12:34:32 +01:00
Sergey Lyubka 639d7a2fd8 Fixing MinGW compilation: pid_t -> process_id_t 2014-03-28 09:18:38 +00:00
Sergey Lyubka 8115ce1d82 Handling SSL_write() error 2014-03-27 11:18:20 +00:00
Sergey Lyubka 3558b587b7 Fixed linking problem for -DMONGOOSE_NO_FILESYSTEM 2014-03-27 10:13:34 +00:00
Sergey Lyubka e21ec58ea8 dav fixes: using auth if auth file is set 2014-03-26 23:13:14 +00:00
Sergey Lyubka 74135bca7d link fixed 2014-03-26 18:58:58 +00:00
Sergey Lyubka d369f31608 return 404 for non-existent propfind URIs 2014-03-26 16:34:55 +00:00
Sergey Lyubka d4b14ca3c1 Better API documentation 2014-03-26 14:13:30 +00:00
Sergey Lyubka f0994896d5 FileSharing doc nitpick 2014-03-26 12:27:44 +00:00
Sergey Lyubka cfdd430b98 Added hexdump_file option 2014-03-26 12:20:02 +00:00
Sergey Lyubka a6c05a9e83 changed handle_propfind() to report only dir entry when listing dir 2014-03-24 20:39:32 +00:00
Sergey Lyubka 94f63787bb Restored void * parameter for iterate_over_connections() 2014-03-24 16:01:45 +00:00
Sergey Lyubka 367f5df328 match_prefix -> mg_match_prefix 2014-03-24 16:00:17 +00:00
Sergey Lyubka f096138d6e Added release note 2014-03-23 20:02:41 +00:00
Sergey Lyubka 0d00cd51e6 Fix #356 2014-03-23 19:59:03 +00:00
Sergey Lyubka b216210a13 Moving lua server side to server.c 2014-03-23 15:33:39 +00:00
Sergey Lyubka 166833f330 Added release notes 2014-03-21 11:47:52 +00:00
Sergey Lyubka 4d6bedfd4e Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-03-20 15:01:18 +00:00
Sergey Lyubka ace8c1736c Added mg_load_dll() 2014-03-20 15:01:05 +00:00
Sergey Lyubka c658a54d18 changed set_absolute_path() signature 2014-03-20 14:58:37 +00:00
Sergey Lyubka 781a1dc25e Merge pull request #352 from ranzhengyuan/some-idea
I think that when the socket invalid, the return value should be -1.
2014-03-20 10:34:18 +00:00
冉正源 3ea3b8dfa4 I think that the socket is invalid, the return should be -1. 2014-03-19 22:32:41 +08:00
Sergey Lyubka a514e143ff updated faw and tutorials 2014-03-19 11:41:01 +00:00
Sergey Lyubka e1486a79e3 Merge pull request #350 from solomonhuang/nodav
Remove MONGOOSE_NO_DAV on DAV_AUTH_FILE options
2014-03-19 11:25:15 +00:00
Sergey Lyubka ee615248e3 Merge pull request #351 from da4089/master
Tyop fix.
2014-03-19 09:33:58 +00:00
David Arnold 687e0e2ee8 Tyop fix. 2014-03-19 04:49:47 +00:00
Solomon Huang 19aefcb7dd Remove MONGOOSE_NO_DAV on DAV_AUTH_FILE options
Building with -DMONGOOSE_NO_DAV will fail.
2014-03-19 01:53:02 +08:00
Sergey Lyubka 6a46244a21 embedding guide for MG_POLL fixed 2014-03-14 08:19:54 +00:00
Sergey Lyubka 60674c11c9 API docs regarding MG_POLL changed 2014-03-13 12:23:53 +00:00
Sergey Lyubka 91c9ab807b Forwarding POST data to the CGI process 2014-03-12 22:43:04 +00:00
Sergey Lyubka 9231c7346c match_prefix -> mg_match_prefix 2014-03-12 18:35:36 +00:00
Sergey Lyubka 20cb3a823a Added ecos & qnx 2014-03-12 14:19:36 +00:00
Sergey Lyubka cbd8d69e6d Added hello world for Lua 2014-03-12 12:47:41 +00:00
Sergey Lyubka f7fcfa6a42 Fixed C# example to work with 5.3 2014-03-12 12:31:53 +00:00
Sergey Lyubka 293c5e5852 Moved screenshots down 2014-03-11 16:59:17 +00:00
Sergey Lyubka ceb866b4ee Using img tags for screenshots 2014-03-11 16:57:38 +00:00
Sergey Lyubka 2f9166e96e Added screenshots for mongoose 2014-03-11 16:51:54 +00:00
Sergey Lyubka fe0e0e69e5 File sharing tutorial wording 2014-03-11 15:36:23 +00:00
Sergey Lyubka db96641c10 Added logo 2014-03-11 06:13:59 +00:00
Sergey Lyubka fe83928ca4 Added logo 2014-03-11 06:11:56 +00:00
Sergey Lyubka 014414f17f Docs links made separate 2014-03-11 06:07:06 +00:00
Sergey Lyubka 6ffe2d7f7a Typo fixed in readme 2014-03-11 06:04:44 +00:00
Sergey Lyubka 30c82d7efe Wording in README changed 2014-03-11 06:03:20 +00:00
Sergey Lyubka 999c4ad26c 5.3 -> 5.4 2014-03-10 17:02:00 +00:00
Sergey Lyubka 30a7965135 added lsp link 2014-03-10 11:21:52 +00:00
Sergey Lyubka a8a017a048 docs updated 2014-03-10 09:23:06 +00:00
Sergey Lyubka eac8ffabc4 docs updated 2014-03-10 09:19:01 +00:00
Sergey Lyubka a7a17711ec Resetting endpoint_type in open_local_endpoint() 2014-03-10 08:35:41 +00:00
Sergey Lyubka 18e28ef879 Added lua includes 2014-03-09 20:22:51 +00:00
Sergey Lyubka ce7515fca9 Merge pull request #349 from jhorneman/master
Exposing Lua support functions
2014-03-09 20:18:49 +00:00
Jurie Horneman d5a78fb664 Fixed bug. 2014-03-09 21:12:34 +01:00
Jurie Horneman 1b3a1b74ac Exposed Lua support functions. 2014-03-09 21:11:06 +01:00
Sergey Lyubka 8dd76e6ef4 Using ns_start_thread() 2014-03-09 19:50:34 +00:00
Sergey Lyubka 976b5f37c1 Added Lua callback 2014-03-09 19:30:41 +00:00
Sergey Lyubka 31489ab7b1 Lua fixes 2014-03-09 19:26:58 +00:00
Sergey Lyubka 1eb59940a4 Re-init rewrites var in convert_uri_to_file_name() 2014-03-09 13:27:39 +00:00
Sergey Lyubka 576d769b56 Corrected links in tutorials 2014-03-09 13:13:25 +00:00
Sergey Lyubka afaba7ee17 Lua calls, birth_time fixed 2014-03-09 13:13:13 +00:00
Sergey Lyubka 40f257ef2b lua build instructions fixed 2014-03-09 13:12:49 +00:00
Sergey Lyubka 5840dc8153 Links updated 2014-03-07 17:08:23 +00:00
Sergey Lyubka 5ed3dafb56 Added warning about PHP 5.5 2014-03-07 14:46:24 +00:00
Sergey Lyubka b99bf306ec fixed image links 2014-03-07 14:40:34 +00:00
Sergey Lyubka 89fae72aa7 added 2014-03-07 14:24:37 +00:00
Sergey Lyubka ab147e8f19 Added MONGOOSE_NO_SSI to MONGOOSE_NO_FILESYSTEM 2014-03-06 13:37:23 +00:00
Sergey Lyubka 81a78f9c8b Added query string to access log 2014-03-05 11:22:23 +00:00
Sergey Lyubka 370aeefdde Added error pages and vhosts to readme 2014-03-05 11:19:18 +00:00
Sergey Lyubka c5407a75f4 #define EV_HANDLER NULL in server.c 2014-03-05 10:12:43 +00:00
Sergey Lyubka 7217aa22f1 Embedded guide 2014-03-05 02:53:19 +00:00
Sergey Lyubka 69bd5f7aa2 SSL_accept logic fix 2014-03-05 02:19:05 +00:00
Sergey Lyubka 41b3c1b23e warning squashed in convert_uri_to_file_name() 2014-03-05 01:59:58 +00:00
Sergey Lyubka f639409533 warning squashed in convert_uri_to_file_name() 2014-03-05 01:59:32 +00:00
Sergey Lyubka 67c44799c8 SSL_accept() error handling fix 2014-03-05 01:57:09 +00:00
Sergey Lyubka 4646e2ad55 Added mjpg example 2014-03-04 20:23:37 +00:00
Sergey Lyubka b77532ea3f Added mjpg example 2014-03-04 20:22:46 +00:00
Sergey Lyubka ed8168e8a1 Added virtual hosts support via url_rewrites 2014-03-04 16:15:02 +00:00
Sergey Lyubka 39e2d2ad55 Appending query_string to the URI when redirecting on HTTP error 2014-03-04 15:01:05 +00:00
Sergey Lyubka 27c6656398 SSL_accept fixes 2014-03-04 13:23:40 +00:00
Sergey Lyubka ffe83051fe build fixed 2014-03-04 13:15:56 +00:00
Sergey Lyubka 93cd77ffad Changed links 2014-03-03 21:18:57 +00:00
Sergey Lyubka 279c06c64c Cleaned up cruft from mongoose.h 2014-03-03 10:56:17 +00:00
Sergey Lyubka 570c8213b6 Cleaned unit test 2014-03-03 10:54:35 +00:00
Sergey Lyubka f521999ea6 Calling EP_CLIENT on close 2014-03-03 10:27:56 +00:00
Sergey Lyubka e41df3cf9c MG_REQ_BEGIN -> MG_REQUEST, killed MG_REQ_END, add MG_REPLY. Restored server test 2014-03-03 10:26:27 +00:00
Sergey Lyubka 65f484291f got rid of multiline comment 2014-03-03 09:45:00 +00:00
Sergey Lyubka 77db89961b mg_stat() fix for paths like X:\ 2014-03-03 01:00:38 +00:00
Sergey Lyubka ad50310bb6 Added index.shtml to index files 2014-03-02 19:49:15 +00:00
Sergey Lyubka 66ec3d1f1e Added defines for popen and pclose, for SSI under Win32 2014-03-02 18:50:02 +00:00
Sergey Lyubka a9da86d05c Mentioned SSI 2014-03-02 18:28:40 +00:00
Sergey Lyubka 5f25a88821 Restored SSI functionality 2014-03-02 18:27:51 +00:00
Sergey Lyubka fd7dc33645 unbroke unit test 2014-03-02 17:20:24 +00:00
Sergey Lyubka 9c22771a43 Merge pull request #345 from nielsegberts/master
Also clean up multi_threaded and websocket_html.c on make clean
2014-03-02 15:48:25 +00:00
Niels Egberts 11640cc3c6 Also clean up multi_threaded and websocket_html.c on make clean 2014-03-02 14:55:34 +00:00
Sergey Lyubka 2264520a2f Example changed to evented API 2014-03-02 13:05:00 +00:00
Sergey Lyubka 2f0b411ddd Added -pthread flag to unix compilation example 2014-03-02 13:00:44 +00:00
Sergey Lyubka e0aceb4f3a Fixed windows build for examples 2014-03-02 12:23:53 +00:00
Sergey Lyubka ea9a60a59a Updated examples to conform to the evented API 2014-03-02 12:16:09 +00:00
Sergey Lyubka 458320cffb Fixed #343 in conn2() for Lua 2014-03-02 10:12:12 +00:00
Sergey Lyubka e0214af422 Using private ns_avprintf() in mg_printf_data() 2014-03-02 10:07:30 +00:00
Sergey Lyubka df5e997ea7 ns_server_poll() waking up by ns_server_wakeup() 2014-03-01 01:55:40 +00:00
Sergey Lyubka 0a3cb63c73 Added mg_wakeup_server() 2014-03-01 01:47:01 +00:00
Sergey Lyubka 37f7f3fb12 Merge pull request #339 from nenioscio/master
Fix issue #338
2014-03-01 01:19:31 +00:00
Sergey Lyubka 2ac970fa16 Moving to event-based API, temporary breaking tests 2014-03-01 01:17:39 +00:00
Sergey Lyubka de35a353d4 pphp -> php 2014-02-27 22:02:18 +00:00
Klaus Wagner dbbda02f70 Fix issue #338 2014-02-27 22:12:46 +01:00
Sergey Lyubka 725c30446c more docs 2014-02-25 20:39:34 +00:00
Sergey Lyubka 60473ae41a more docs 2014-02-25 20:39:00 +00:00
Sergey Lyubka 951d20f904 Added coniditional to use net_skeleton separately 2014-02-25 18:52:23 +00:00
Sergey Lyubka 85a6abd58b Clearing flags in close_local_endpoint() 2014-02-25 12:02:45 +00:00
Sergey Lyubka 2fbe09ad7c Restored jni build dir for Android 2014-02-25 08:12:42 +00:00
Sergey Lyubka 23325efab2 Added time.h 2014-02-25 08:10:19 +00:00
Sergey Lyubka 107902bae4 add_to_set(): fix around non-negative SOCKET on Windows 2014-02-24 08:22:40 +00:00
Sergey Lyubka 9bbf4c8384 Fixed problem with mg_printf_data() 2014-02-22 16:11:47 +00:00
Sergey Lyubka c39a712540 Win32 build fix 2014-02-21 14:13:06 +00:00
Sergey Lyubka e9f6111ba1 re-added signal.h 2014-02-20 17:47:07 +00:00
Sergey Lyubka ba267a32fd squashed warning in read_from_socket 2014-02-20 17:44:47 +00:00
Sergey Lyubka f64ebbe0b1 Guarded on_cgi_data() call by ifdef, thanks to Pavel Pimenov 2014-02-20 12:33:46 +00:00
Sergey Lyubka 8865dd5e84 Moved unit_test.c to examples 2014-02-20 12:13:59 +00:00
Sergey Lyubka ce57f47de0 Added missing signal.h 2014-02-20 09:49:42 +00:00
Sergey Lyubka 48be2dbc2a Added conditional for handling idle websocket conns 2014-02-20 07:32:33 +00:00
Sergey Lyubka 8905522a94 Setting last_io_time in ns_add_sock() 2014-02-20 06:26:44 +00:00
Sergey Lyubka eea1680cf9 open_cgi_endpoint() strrchr fix 2014-02-19 16:44:01 +00:00
Sergey Lyubka d5004cb7eb PF_INET -> AF_INET, unbreaks FreeBSD 2014-02-19 16:39:30 +00:00
Sergey Lyubka 1d1a05bf39 Embedded net_skeleton 2014-02-19 16:38:53 +00:00
Sergey Lyubka 2009069037 merge 2014-02-19 16:36:27 +00:00
Sergey Lyubka 8f7703c2a9 moved to net_skeleton 2014-02-19 16:34:51 +00:00
Sergey Lyubka 8e87c4f013 Merge pull request #329 from abimaelmartell/fix/freebsd-compilation
fix compilation under FreeBSD
2014-02-14 23:46:07 +00:00
Abimael Martell 639a6dc2a8 fix compilation under FreeBSD 2014-02-14 13:21:14 -08:00
Sergey Lyubka cead9a04ca Typo fixed 2014-02-11 15:21:54 +00:00
Sergey Lyubka e528093027 Formatted example 2014-02-11 15:20:19 +00:00
Sergey Lyubka c432b783fb Formatted example 2014-02-11 15:19:18 +00:00
Sergey Lyubka e06976836f Typo fixed 2014-02-11 15:18:20 +00:00
Sergey Lyubka c9f7407573 More comments in example 2014-02-11 15:16:55 +00:00
Sergey Lyubka a40e59dbe6 URL corrected 2014-02-11 09:56:10 +00:00
Sergey Lyubka 61ee3d36d0 Merge pull request #324 from eichin/patch-1
doc typos
2014-02-11 09:55:47 +00:00
Mark Eichin 94dbf21ffb doc typos
just saw them while reading...
2014-02-11 03:41:09 -05:00
Sergey Lyubka 998425b14b Styling changes for hexdump 2014-02-10 10:21:57 +00:00
Sergey Lyubka 6900dd733a Merge pull request #322 from pavel-pimenov/master
Fix copy-paste for MONGOOSE_HEXDUMP feature
2014-02-10 09:48:26 +00:00
Pavel Pimenov 9e4cd054c8 Fix copy-paste for MONGOOSE_HEXDUMP feature 2014-02-10 12:00:51 +04:00
Sergey Lyubka 5dc317fcb0 Added -DMONGOOSE_HEXDUMP feature 2014-02-09 20:57:11 +00:00
Sergey Lyubka fbd5d77ce8 Added note about CGI env vars 2014-02-07 20:23:37 +00:00
Sergey Lyubka 6f8751b479 chat server is going to be in its own dir 2014-02-07 13:04:02 +00:00
Sergey Lyubka 877c4dc698 websocket example responses: 15 times -> 3 times 2014-02-07 13:03:28 +00:00
Sergey Lyubka 2dfc79129a better lua build 2014-02-07 13:03:11 +00:00
Sergey Lyubka 416f4bf10f Merge pull request #321 from pavel-pimenov/master
Add #ifndef MONGOOSE_NO_FILESYSTEM
2014-02-07 07:45:20 +00:00
Pavel Pimenov f9a99530ff Add #ifndef MONGOOSE_NO_FILESYSTEM 2014-02-07 11:02:12 +04:00
Sergey Lyubka 940b65e4db Merge pull request #320 from xant/upstream
added a new handler callback to be used when the http connection is being closed
2014-02-06 21:00:17 +00:00
Andrea Guzzo b22f7d3a49 added a new handler callback to be used when the http connection is being closed
so that in case of connections closed prematurely, the user can clear resources
eventually bound to the connection using the 'connection_param' member of the
mg_connection structure
2014-02-06 11:22:13 +01:00
Sergey Lyubka eaa0e26a85 Starting to rebuild chat example on Lua 2014-02-06 10:18:30 +00:00
Sergey Lyubka a3a63f7e56 Using mg_connection::callback_param for mg_iterate_over_connection() 2014-02-06 10:00:20 +00:00
Sergey Lyubka c9036f3ac0 Added workaround for non-compliant runtimes in alloc_vprintf() 2014-02-05 12:33:36 +00:00
Sergey Lyubka 60858d7fe5 NO_WEBSOCKET -> MONGOOSE_NO_WEBSOCKET 2014-02-04 19:57:00 +00:00
Sergey Lyubka 1c914bde1d Merge pull request #316 from To1ne/master
use `mg_set_request_handler`
2014-02-04 11:55:52 -08:00
Toon Claes a487332aa0 use mg_set_request_handler 2014-02-04 20:48:38 +01:00
Sergey Lyubka 3987d4d560 Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-02-04 18:13:10 +00:00
Sergey Lyubka 8d3181e0bf examples/html -> examples/chat_html_root 2014-02-04 18:13:02 +00:00
Sergey Lyubka 0f1a59aeb4 Increased env buf size 4096 -> 8192. Passing TMPDIR to CGI. 2014-02-04 18:11:56 +00:00
Sergey Lyubka 2cdf259940 Merge pull request #315 from To1ne/patch-1
Correct return values: PROCESSED or CALL_AGAIN
2014-02-04 06:51:41 -08:00
Toon Claes 0187fe943a Correct return values: PROCESSED or CALL_AGAIN 2014-02-04 15:24:32 +01:00
Sergey Lyubka a34d1ec00a Better handling of MONGOOSE_NO_SOCKETPAIR 2014-02-04 14:04:54 +00:00
Sergey Lyubka e70d98b7b0 Added fixes for eCos 2014-02-04 09:59:53 +00:00
Sergey Lyubka bea012a3c1 Removed redundant addenv call 2014-02-04 08:06:49 +00:00
Sergey Lyubka eb2d470f65 Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-02-03 17:54:46 +00:00
Sergey Lyubka 652c352394 Passing SERVER_NAME, TMP and TEMP env vars to CGI. 2014-02-03 17:54:31 +00:00
Sergey Lyubka f1b37b3101 Merge pull request #313 from xant/upstream
bugfix: set the correct status if we still have pending data
2014-02-03 04:52:10 -08:00
Sergey Lyubka 81b9876af8 5.2 -> 5.3 2014-02-01 16:53:09 +00:00
xant 9e118c8a31 bugfix: set the correct status if we still have pending data 2014-01-31 19:09:12 +01:00
90 changed files with 6458 additions and 3823 deletions

No files matched your search

+3 -3
View File
@@ -1,11 +1,11 @@
Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
Copyright (c) 2013 Cesanta Software Limited
Copyright (c) 2013-2015 Cesanta Software Limited
All rights reserved
This code is dual-licensed: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation. For the terms of this
license, see http://www.gnu.org/licenses.
license, see <http://www.gnu.org/licenses>.
You are free to use this code under the terms of the GNU General
Public License, but WITHOUT ANY WARRANTY; without even the implied
@@ -13,4 +13,4 @@ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
Alternatively, you can license this code under a commercial
license, as set out in http://cesanta.com/products.html.
license, as set out in <http://cesanta.com/>.
+54 -26
View File
@@ -1,30 +1,49 @@
# Mongoose Web Server
# <img src="http://cesanta.com/images/mongoose_logo.png" width="64" height="64"> Mongoose Web Server
Mongoose is the most easy to use web server on the planet. A web server of choice for Web developers (PHP, Ruby, Python, etc) and Web designers.
Mongoose is built on top of Libmongoose embedded library, which can turn
anything into a web server in 5 minutes worth of effort and few lines of code.
Libmongoose is used to serve Web GUI on embedded devices, implement RESTful
services, RPC frameworks (e.g. JSON-RPC), handle telemetry data exchange, and
perform many other tasks in various different industries including aerospace,
manufacturing, finance, research, automotive, gaming, IT.
Mongoose is easy to use, powerful, embeddable web server.
* [Downloads](http://cesanta.com/downloads.html)
* [Documentation](http://cesanta.com/docs.html)
* [Mailing list](http://groups.google.com/group/mongoose-users)
* [Downloads](http://cesanta.com/products.shtml)
* [Documentation](http://cesanta.com/docs.shtml)
Check out Fossa - our [embedded multi-protocol library](https://github.com/cesanta/fossa) with TCP,UDP,HTTP,Websocket,MQTT,DNS support, designed for Internet Of Things!
# Features
- Works on Windows, Mac, UNIX/Linux, iPhone, Android, and many other platforms
- CGI, SSL, Digest auth, Websocket, WEbDAV, Resumed download, URL rewrite, file blacklist, IP-based ACL, Windows service
- Scripting and database support ([Lua](http://lua.org) +
[Sqlite](http://sqlite.org), see
[dirscan.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/dirscan.lp) ),
which provides ready to go, powerful web development platform in
one single-click executable with no dependencies
- Works on Windows, Mac, UNIX/Linux, iPhone, Android eCos, QNX
and many other platforms
- CGI, SSI, SSL, Digest auth, Websocket, WEbDAV, Resumed download,
URL rewrite, file blacklist
- Custom error pages, Virtual hosts, IP-based ACL, Windows service,
HTTP/HTTPS client
- Simple and clean
[embedding API](https://github.com/cesanta/mongoose/blob/master/mongoose.h).
The source is in single
[mongoose.c](https://github.com/cesanta/mongoose/blob/master/mongoose.c) file
to make embedding easy
- Extremely lightweight, has a core of under 40kB and tiny runtime footprint
- Asyncronouns, non-blocking core supporting single- or multi-threaded usage
- On the market since 2004, stable, mature and tested,
has several man-years invested in continuous improvement and refinement
- Professional support and consultancy available from
[Cesanta Software](http://cesanta.com), developer of Mongoose
- Asynchronous, non-blocking core supporting single- or multi-threaded usage
- On the market since 2004 with over 1 million cumulative downloads
- Stable, mature and tested, has several man-years invested
in continuous improvement and refinement
# Screenshots
Download, double-click to start, run browser -- that's all!
![shot1](http://cesanta.com/images/tut_sharing/tut1.png)
![shot2](http://cesanta.com/images/tut_sharing/tut2.png)
![shot3](http://cesanta.com/images/tut_sharing/tut3.png)
![shot4](http://cesanta.com/images/tut_sharing/tut4.png)
# Acknowledgements
@@ -47,14 +66,23 @@ F.Morenius, and 10 others.
# Licensing
Mongoose is released under
[GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html).
Businesses have an option to get non-restrictive, royalty-free commercial
license and professional support from
[Cesanta Software](http://cesanta.com).
Mongoose is released under commercial and
[GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) open
source licenses. The GPLv2 open source License does not generally permit
incorporating this software into non-open source programs.
For those customers who do not wish to comply with the GPLv2 open
source license requirements,
[Cesanta Software](http://cesanta.com) offers a full,
royalty-free commercial license and professional support
without any of the GPL restrictions.
[Frozen JSON parser](https://github.com/cesanta/frozen),
[Super Light Regular Expression library](https://github.com/cesanta/slre),
[Super Light DNS Resolver](https://github.com/cesanta/sldr)
are other projects by Cesanta Software, developed with the same philosophy
of functionality and simplicity.
# Other products by Cesanta Software: simple and effective
- [SSL Wrapper](https://github.com/cesanta/ssl_wrapper) - application to
secure network communications
- [Frozen](https://github.com/cesanta/frozen) - JSON parser and generator
- [SLRE](https://github.com/cesanta/slre) - Super Light Regular Expression
library
- [Net Skeleton](https://github.com/cesanta/net_skeleton) - framework for
building network applications
- [SLDR](https://github.com/cesanta/sldr) - Super Light DNS Resolver
+95 -57
View File
@@ -7,8 +7,7 @@ there is not enough memory. `server_param`: Could be any pointer, or NULL.
This pointer will be passed
to the callback functions as `struct mg_connection::server_param` field.
A common use case is to pass `this` pointer of the C++ wrapper class
as `user_param`, to let the callback get the pointer to the C++
object.
as `user_param`, to let the callback get the pointer to the C++ object.
Note that this function doesn't make the
server instance to serve. Serving is done by `mg_poll_server()` function.
@@ -17,10 +16,7 @@ When server instance is created, it contains an information about
the configuration and the state of each connection.
Server instance is capable on listening on only one port. After creation,
`struct mg_server` has a list
of active connections, initially empty. It has a list of URI handlers,
initially empty, and configuration parameters. Configuration can be
altered by `mg_set_option()`, and new URI handlers could be added by
`mg_add_uri_handler()`.
of active connections and configuration parameters.
Side-effect: on UNIX, `mg_create_server()` ignores SIGPIPE signals. If custom
processing is required SIGPIPE, signal handler must be set up after
@@ -38,57 +34,84 @@ server pointer a NULL pointer.
const char mg_set_option(struct mg_server *server, const char *name,
const char *value);
Sets a particular server option. Please refer to a separate documentation page
that lists all available option names. Note that at least one option,
Sets a particular server option. Note that at least one option,
`listening_port`, must be specified. To serve static files, `document_root`
must be specified too. If `document_root` option is left unset, Mongoose
will not access filesystem at all. This function returns NULL if option was
will not access filesystem at all. `mg_set_option()` returns NULL if option was
set successfully, otherwise it returns human-readable error string. It is
allowed to call `mg_set_option()` by the same thread that does
`mg_poll_server()` (an IO thread) and change server configuration while it
`mg_poll_server()` (Mongoose thread) and change server configuration while it
is serving, in between `mg_poll_server()` calls.
void mg_poll_server(struct mg_server *server, int milliseconds);
int mg_poll_server(struct mg_server *server, int milliseconds);
This function performs one iteration of IO loop by iterating over all
Performs one iteration of IO loop by iterating over all
active connections, performing `select()` syscall on all sockets with a timeout
of `milliseconds` number of milliseconds. When `select()` returns, Mongoose
of `milliseconds`. When `select()` returns, Mongoose
does an IO for each socket that has data to be sent or received. Application
code must call `mg_poll_server()` in a loop. It is an error to have more then
one thread calling `mg_poll_server()`, `mg_set_option()` or any other function
that take `struct mg_server *` parameter. Mongoose does not
mutex-protect `struct mg_server *`, therefore the best practice is
to call server management functions from the same thread (an IO thread).
mutex-protect `struct mg_server *`, therefore only single thread
(Mongoose thread) should make Mongoose calls.
void mg_set_auth_handler(struct mg_server *, mg_handler_t handler);
`mg_poll_server()` calls user-specified event handler when certain events
occur. Sequence of events for the accepted connection is this:
Sets authorization handler. Called by Mongoose on each request, before
performing any other action. Handler can return either `MG_AUTH_OK` or
`MG_AUTH_FAIL`. If `handler` returns `MG_AUTH_FAIL`, then Mongoose sends
digest authorization request to the client. If `handler returns `MG_AUTH_OK`,
then mongoose proceeds with handling the request. Handler function can use
`mg_authorize_digest()` function to verify authorization, or implement any other
custom authorization mechanism.
* `MG_AUTH` - Mongoose asks whether this connection is authorized. If event
handler returns `MG_FALSE`, then Mongoose does not serve the request but
sends authorization request to the client. If `MG_TRUE` is returned,
then Mongoose continues on with the request.
* `MG_REQUEST` - Mongoose asks event handler to serve the request. If
event handler serves the request by sending a reply,
it should return `MG_TRUE`. Otherwise,
it should return `MG_FALSE` which tells Mongoose that request is not
served and Mongoose should serve it. For example, event handler might
choose to serve only RESTful API requests with URIs that start with
certain prefix, and let Mongoose serve all static files.
If event handler decides to serve the request, but doesn't have
all the data at the moment, it should return `MG_MORE`. That tells
Mongoose to keep the connection open after callback returns.
void mg_set_request_handler(struct mg_server *, mg_handler_t handler);
`mg_connection::connection_param` pointer is a placeholder to keep
user-specific data. For example, handler could decide to open a DB
connection and store DB connection handle in `connection_param`.
* `MG_POLL` is sent to every connection on every iteration of
`mg_poll_server()`. Event handler should return `MG_FALSE` to ignore
this event. If event handler returns `MG_TRUE`, then Mongoose assumes
that event handler has finished sending data, and Mongoose will
close the connection.
* `MG_HTTP_ERROR` sent when Mongoose is about to send HTTP error back
to the client. Event handler can choose to send a reply itself, in which
case event handler must return `MG_TRUE`. Otherwise, event handler must
return `MG_FALSE`.
* `MG_CLOSE` is sent when the connection is closed. This event is used
to cleanup per-connection state stored in `connection_param`
if it was allocated. Event handler return value is ignored.
Sets a request handler. When set, `handler` will be called for each request.
Possible return values from a handler function are `MG_REQUEST_NOT_PROCESSED`,
`MG_REQUEST_PROCESSED` and `MG_REQUEST_CALL_AGAIN`. If handler returns
`MG_REQUEST_NOT_PROCESSED`, mongoose will proceed with handling the request.
If handler returns `MG_REQUEST_PROCESSED`, that signals mongoose that handler
has already processed the connection, and mongoose will skip to the next
request. `MG_REQUEST_CALL_AGAIN` tells mongoose to call request handler
again and again on each `mg_poll_server()` iteration.
Sequence of events for the client connection is this:
When mongoose buffers in HTTP request and successfully parses it, it calls
appropriate URI handler immediately for GET requests. For POST requests,
* `MG_CONNECT` sent when Mongoose has connected to the remote host.
This event is sent to the connection initiated by `mg_connect()` call.
Connection status is held in `mg_connection::status_code`: if zero,
then connection was successful, otherwise connection was not established.
User should send a request upon successful connection.
Event handler should return `MG_TRUE` if connection was successful and
HTTP request has been sent. Otherwise, it should send `MG_FALSE`.
* `MG_REPLY` is sent when response has been received from the remote host.
If event handler sends another request, then it should return `MG_TRUE`.
Otherwise it should return `MG_FALSE` and Mongoose will close the connection.
* `MG_CLOSE` same as for the accepted connection.
When mongoose buffers in HTTP request and successfully parses it, it sends
`MG_REQUEST` event for GET requests immediately. For POST requests,
Mongoose delays the call until the whole POST request is buffered in memory.
POST data is available to the callback as `struct mg_connection::content`,
and POST data length is in `struct mg_connection::content_len`.
Note that websocket connections are treated the same way. Mongoose buffers
websocket frame in memory, and calls URI handler when frame is fully
websocket frame in memory, and calls event handler when frame is fully
buffered. Frame data is available `struct mg_connection::content`, and
data length is in `struct mg_connection::content_len`, i.e. very similar to
the POST request. `struct mg_connection::is_websocket` flag indicates
@@ -96,18 +119,10 @@ whether the request is websocket or not. Also, for websocket requests,
there is `struct mg_connection::wsbits` field which contains first byte
of the websocket frame which URI handler can examine. Note that to
reply to the websocket client, `mg_websocket_write()` should be used.
To reply to the plain HTTP client, `mg_write_data()` should be used. Note that
websocket handler must return either `MG_CLIENT_CLOSE` or `MG_CLIENT_CONTINUE`
value.
To reply to the plain HTTP client, `mg_write_data()` should be used.
void mg_set_http_error_handler(struct mg_server *, mg_handler_t);
Return value: number of active connections.
Adds HTTP error handler. An actual HTTP error is passed as
`struct mg_connection::status_code` parameter.
Hanlder should return either `MG_ERROR_PROCESSED` or `MG_ERROR_NOT_PROCESSED`.
If handler returns `MG_ERROR_NOT_PROCESSED`, it
means a handler has not processed the connection, and mongoose proceeds
with sending HTTP error to the client. Otherwise, mongoose does nothing.
const char **mg_get_valid_option_names(void);
@@ -123,17 +138,14 @@ given parameter name is not valid, NULL is returned. For valid names, return
value is guaranteed to be non-NULL. If parameter is not set, zero-length string
is returned.
void mg_wakeup_server_ex(struct mg_server *, mg_handler_t func,
const char *fmt, ...);
int mg_iterate_over_connections(struct mg_server *,
void (*func)(struct mg_connection *, void *),
void *param);
This is an interface primarily designed to push arbitrary data to websocket
connections at any time. This function could be called from any thread. When
it returns, an IO thread called `func()` on each active connection,
passing `param` as an extra parameter. It is allowed to call `mg_send_data()` or
`mg_websocket_write()` within a callback, cause `func` is executed in the
context of the IO thread.
Sends string message to a server. Function `func` is called for every active
connection. String message is passed in `struct mg_connection::callback_param`.
This function is designed to push data to the connected clients, and
can be called from any thread. There is a limitation on the length of
the message, currently at 8 kilobytes.
void mg_send_status(struct mg_connection *, int status_code);
void mg_send_header(struct mg_connection *, const char *name,
@@ -161,6 +173,15 @@ a response body. Mongoose provides functions for all three parts:
Mongoose will call a handler repeatedly after each socket write.
<!-- -->
void mg_send_file(struct mg_connection *, const char *path);
Tells Mongoose to serve given file. Mongoose handles file according to
it's extensions, i.e. Mongoose will invoke CGI script if `path` has CGI
extension, it'll render SSI file if `path` has SSI extension, etc. If `path`
points to a directory, Mongoose will show directory listing. If this function
is used, no calls to `mg_send*` or `mg_printf*` functions must be made, and
event handler must return `MG_MORE`.
int mg_websocket_write(struct mg_connection* conn, int opcode,
const char *data, size_t data_len);
@@ -201,8 +222,25 @@ the length of the fetched value, or 0 if variable not found.
Add, edit or delete the entry in the passwords file.
This function allows an application to manipulate .htpasswd files on the
fly by adding, deleting and changing user records. This is one of the
several ways of implementing authentication on the server side. For another,
cookie-based way please refer to the examples/chat.c in the source tree.
several ways of implementing authentication on the server side.
If password is not NULL, entry is added (or modified if already exists).
If password is NULL, entry is deleted.
Return: 1 on success, 0 on error.
int mg_parse_multipart(const char *buf, int buf_len,
char *var_name, int var_name_len,
char *file_name, int file_name_len,
const char **data, int *data_len);
Parses a buffer that contains multipart form data. Stores chunk name
in a `var_name` buffer. If chunk is an uploaded file, then `file_name`
will have a file name. `data` and `data_len` will point to the chunk data.
Returns number of bytes to skip to the next chunk.
struct mg_connection *mg_connect(struct mg_server *server,
const char *host, int port, int use_ssl);
Create connection to the remote host. Returns `NULL` on error, non-null
if the connection has been scheduled for connection. Upon a connection,
Mongoose will send `MG_CONNECT` event to the event handler.
+2 -2
View File
@@ -7,7 +7,7 @@ Note : You dont need root access to run mongoose on Android.
- Clone Mongoose Git repo
- Download the Android NDK from [http://developer.android.com/tools/sdk/ndk/index.html](http://developer.android.com/tools/sdk/ndk/index.html)
- Run `/path-to-ndk/ndk-build -C /path-to-mongoose/build`
- Run `/path-to-ndk/ndk-build -C /path/to/mongoose`
That should generate mongoose/lib/armeabi/mongoose
- Using the adb tool (you need to have Android SDK installed for that),
push the generated mongoose binary to `/data/local` folder on device.
@@ -15,7 +15,7 @@ Note : You dont need root access to run mongoose on Android.
- To test if the server is running fine, visit your web-browser and
navigate to `http://127.0.0.1:8080` You should see the `Index of /` page.
![screenshot](https://a248.e.akamai.net/camo.github.com/b88428bf009a2b6141000937ab684e04cc8586af/687474703a2f2f692e696d6775722e636f6d2f62676f6b702e706e67)
![screenshot](http://cesanta.com/images/android_build.png)
Notes:
+34
View File
@@ -0,0 +1,34 @@
How To Create Basic Website With Mongoose
===========================================
## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`:
![screenshot](http://cesanta.com/images/tut_basic/tut1.png)
## 2. Inside `my_website` directory, create a new file called "index". This will be the default web page shown when the website is visited.
![screenshot](http://cesanta.com/images/tut_basic/tut2.png)
## 3. Open index file with your favorite editor (for example, Notepad) and enter some HTML code:
![screenshot](http://cesanta.com/images/tut_basic/tut3.png)
## 4. Save this file as `index.html`:
![screenshot](http://cesanta.com/images/tut_basic/tut4.png)
## 5. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside `my_website` directory:
![screenshot](http://cesanta.com/images/tut_basic/tut5.png)
## 6. Double-click mongoose executable. An icon will appear on a system tray in the bottom right corner of the desktop:
![screenshot](http://cesanta.com/images/tut_basic/tut6.png)
## 7. Click on the mongoose icon and choose "Go to my address" menu:
![screenshot](http://cesanta.com/images/tut_basic/tut7.png)
## 8. A browser will popup displaying `index.html` file. Now, you can expand your website by adding more content.
![screenshot](http://cesanta.com/images/tut_basic/tut8.png)
+102 -37
View File
@@ -13,34 +13,81 @@ Embedding Mongoose is done in two steps:
Here's a minimal application `app.c` that embeds mongoose:
#include "mongoose.h"
int main(void) {
struct mg_server *server = mg_create_server(NULL);
mg_set_option(server, "document_root", ".");
mg_set_option(server, "listening_port", "8080");
for (;;) mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
struct mg_server *server = mg_create_server(NULL, NULL);
mg_set_option(server, "document_root", "."); // Serve current directory
mg_set_option(server, "listening_port", "8080"); // Open port 8080
for (;;) {
mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
}
mg_destroy_server(&server);
return 0;
}
To compile it, put `mongoose.c`, `mongoose.h` and `minimal.c` into one
To compile it, put `mongoose.c`, `mongoose.h` and `app.c` into one
folder, start terminal on UNIX or Visual Studio command line prompt on Windows,
and run the following command:
cc app.c mongoose.c -o app # on Unix
cl app.c mongoose.c /TC /MD # on Windows
cc app.c mongoose.c -pthread -o app # on Unix
cl.exe app.c mongoose.c /TC /MD # on Windows
When run, this simple application opens port 8080 and serves static files,
CGI files and lists directory content in the current working directory.
Mongoose can call user-defined functions when certain URIs are requested.
These functions are _called uri handlers_. `mg_add_uri_handler()` registers
an URI handler, and there is no restriction exist on the number of URI handlers.
Also, mongoose can call a user-defined function when it is about to send
HTTP error back to client. That function is called _http error handler_ and
can be registered by `mg_set_http_error_handler()`. Handlers are called
by Mongoose with `struct mg_connection *` pointer as a parameter, which
It is possible to generate HTML page content. Mongoose can call user-defined
function when certain events occur.
That function is called _an event handler_, and it is the second parameter
to `mg_create_server()` function. Here is the example event handler function:
int event_handler(struct mg_connection *conn, enum mg_event ev) {
switch (ev) {
case MG_AUTH: return MG_TRUE;
default: return MG_FALSE;
}
}
Event handler is called by Mongoose with `struct mg_connection *`
pointer and an event number. `struct mg_connection *conn`
has all information about the request: HTTP headers, POST or websocket
data buffer, etcetera.
data buffer, etcetera. `enum mg_event ev` tells which exactly event is sent.
For each event, an event handler returns a value which tells Mongoose how
to behave.
The sequence of events for every connection is this:
* `MG_AUTH` - Mongoose asks whether this connection is authorized. If event
handler returns `MG_FALSE`, then Mongoose does not serve the request but
sends authorization request to the client. If `MG_TRUE` is returned,
then Mongoose continues on with the request.
* `MG_REQUEST` - Mongoose asks event handler to serve the request. If
event handler serves the request by sending a reply,
it should return `MG_TRUE`. Otherwise,
it should return `MG_FALSE` which tells Mongoose that request is not
served and Mongoose should serve it. For example, event handler might
choose to serve only RESTful API requests with URIs that start with
certain prefix, and let Mongoose serve all static files.
If event handler decides to serve the request, but doesn't have
all the data at the moment, it should return `MG_MORE`. That tells
Mongoose to keep the connection open after callback returns.
`mg_connection::connection_param` pointer is a placeholder to keep
user-specific data. For example, handler could decide to open a DB
connection and store DB connection handle in `connection_param`.
* `MG_POLL` is sent to every connection on every iteration of
`mg_poll_server()`. Event handler should return `MG_FALSE` to ignore
this event. If event handler returns `MG_TRUE`, then Mongoose assumes
that event handler has finished sending data, and Mongoose will
close the connection.
* `MG_HTTP_ERROR` sent when Mongoose is about to send HTTP error back
to the client. Event handler can choose to send a reply itself, in which
case event handler must return `MG_TRUE`. Otherwise, event handler must
return `MG_FALSE`
* `MG_CLOSE` is sent when the connection is closed. This event is used
to cleanup per-connection state stored in `connection_param`
if it was allocated.
Let's extend our minimal application example and
create an URI that will be served by user's C code. The app will handle
@@ -50,22 +97,38 @@ http://127.0.0.1:8080/hello will say hello, and here's the code:
#include <string.h>
#include "mongoose.h"
static int handle_hello(struct mg_connection *conn) {
static const char *reply = "Hello world!\n";
mg_write_data(conn, reply, strlen(reply));
return 1;
static int event_handler(struct mg_connection *conn, enum mg_event ev) {
if (ev == MG_AUTH) {
return MG_TRUE; // Authorize all requests
} else if (ev == MG_REQUEST && !strcmp(conn->uri, "/hello")) {
mg_printf_data(conn, "%s", "Hello world");
return MG_TRUE; // Mark as processed
} else {
return MG_FALSE; // Rest of the events are not processed
}
}
int main(void) {
struct mg_server *server = mg_create_server(NULL);
struct mg_server *server = mg_create_server(NULL, event_handler);
mg_set_option(server, "document_root", ".");
mg_set_option(server, "listening_port", "8080");
mg_add_uri_handler(server, "/hello", &handle_hello);
for (;;) mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
for (;;) {
mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
}
mg_destroy_server(&server);
return 0;
}
## Example code
Mongoose source code contains number of examples, located in the
[examples](https://github.com/cesanta/mongoose/blob/master/examples/) directory.
To build any example, go to the respective directory and run `make`.
## Compilation flags
Below is the list of compilation flags that enable or disable certain
features. By default, some features are enabled, and could be disabled
by setting appropriate `NO_*` flag. Features that are disabled by default
@@ -73,6 +136,12 @@ could be enabled by setting appropriate `USE_*` flag. Bare bones Mongoose
is quite small, about 30 kilobytes of compiled x86 code. Each feature adds
a couple of kilobytes to the executable size, and also has some runtime penalty.
Note that some flags start with `NS_` prefix. This is because Mongoose uses
[Net Skeleton](http://github.com/cesanta/net_skeleton) as a low-level
networking engine. If user code has `#include <net_skeleton.h>`, then
all Net Skeleton functions will be available too.
-DMONGOOSE_NO_AUTH Disable MD5 authorization support
-DMONGOOSE_NO_CGI Disable CGI support
-DMONGOOSE_NO_DAV Disable WebDAV support
@@ -82,27 +151,23 @@ a couple of kilobytes to the executable size, and also has some runtime penalty.
-DMONGOOSE_NO_LOGGING Disable access/error logging
-DMONGOOSE_NO_THREADS
-DMONGOOSE_NO_WEBSOCKET Disable WebSocket support
-DMONGOOSE_NO_USER No concept of a user on used platform.
(Platform does not provide getpwnam, setgid or setuid)
-DMONGOOSE_USE_IDLE_TIMEOUT_SECONDS=X Idle connection timeout, default is 30
-DMONGOOSE_USE_IPV6 Enable IPv6 support
-DMONGOOSE_USE_LUA Enable Lua scripting
-DMONGOOSE_USE_LUA_SQLITE3 Enable sqlite3 binding for Lua
-DMONGOOSE_USE_SSL Enable SSL
-DMONGOOSE_USE_POST_SIZE_LIMIT=X POST requests larger than X will be
rejected, not set by default
-DMONGOOSE_USE_EXTRA_HTTP_HEADERS=X Append X to the HTTP headers
for static files, empty by default
-DMONGOOSE_USE_STACK_SIZE=X Let mg_start_thread() use stack
size X, default is OS default
-DMONGOOSE_ENABLE_DEBUG Enables debug messages on stdout, very noisy
Mongoose source code contains a well-commented example code, listed below:
* [hello.c](https://github.com/cesanta/mongoose/blob/master/examples/hello.c)
a minimalistic hello world example
* [post.c](https://github.com/cesanta/mongoose/blob/master/examples/post.c)
shows how to handle form input
* [upload.c](https://github.com/cesanta/mongoose/blob/master/examples/post.c)
shows how to upload files
* [websocket.c](https://github.com/cesanta/mongoose/blob/master/examples/websocket.c) demonstrates websocket usage
* [auth.c](https://github.com/cesanta/mongoose/blob/master/examples/websocket.c) demonstrates API-controlled Digest authorization
-DNS_ENABLE_DEBUG Enables debug messages on stdout, very noisy
-DNS_ENABLE_SSL Enable SSL
-DNS_ENABLE_IPV6 Enable IPv6 support
-DNS_ENABLE_HEXDUMP Enables hexdump of sent and received traffic
-DNS_STACK_SIZE=X Sets stack size to X for ns_start_thread()
-DNS_DISABLE_THREADS Disable threads support
-DNS_DISABLE_SOCKETPAIR For systems without loopback interface
-DMONGOOSE_SEND_NS_EVENTS Send Net Skeleton events to the event handler
in addition to the Mongoose events
+24 -2
View File
@@ -1,6 +1,28 @@
# Mongoose FAQ
### PHP doesn't work: getting empty page, or 'File not found' error
## My Antivirus Software reports Mongoose as a security threat
Mongoose doesn't contain any malicious logic. Antivirus reports a
[false positive](http://en.wikipedia.org/wiki/Type_I_and_type_II_errors#False_positive_error).
This is when certain byte sequence in Mongoose accidentally matches
virus signature in the Antivirus database.
## Download page doesn't work
Please make sure Javascript is enabled in your browser, and that the
antivirus software is not blocking the download.
## MacOS message: "Mongoose.app is damaged and can’t be opened. You should move it to the Trash"
This happens on newer MacOS systems. The reason for the message
is the fact Mongoose.app is not digitally signed.
Mongoose download procedure changes the app on the fly by injecting
user information in the binary, making any prior digital signature void.
Open "System Preferences" -> "Security" and set "Allow apps downloaded from"
to "Anywhere". Revert the settings once Mongoose is installed.
## PHP doesn't work: getting empty page, or 'File not found' error
The reason for that is wrong paths to the interpreter. Remember that with PHP,
correct interpreter is `php-cgi.exe` (`php-cgi` on UNIX). Solution: specify
@@ -8,7 +30,7 @@ full path to the PHP interpreter, e.g.:
mongoose -cgi_interpreter /full/path/to/php-cgi
### Mongoose fails to start
## Mongoose fails to start
If Mongoose exits immediately when run, this
usually indicates a syntax error in the configuration file
+18
View File
@@ -0,0 +1,18 @@
How To Share Files With Mongoose
===========================================
## 1. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside the directory you want to share:
![screenshot](http://cesanta.com/images/tut_sharing/tut1.png)
## 2. Double-click mongoose executable. A browser will start automatically, an icon will appear on a system tray in the bottom right corner of the desktop:
![screenshot](http://cesanta.com/images/tut_sharing/tut2.png)
## 3. Click on the mongoose icon
![screenshot](http://cesanta.com/images/tut_sharing/tut3.png)
## 4. Click on "Go to my address" to launch a browser locally. Or, to access a folder from another machine, launch a browser and type in the URL:
![screenshot](http://cesanta.com/images/tut_sharing/tut4.png)
+9 -17
View File
@@ -4,16 +4,16 @@ Pre-built Windows and Mac mongoose binaries support Lua Server Pages
functionality.
That means it is possible to write PHP-like scripts with mongoose
using Lua programming language instead of PHP. Lua is known
for it's speed and small size. Mongoose uses Lua version 5.2.1, the
for it's speed and small size. Mongoose uses Lua version 5.2.3, the
documentation for it can be found at
[Lua 5.2 reference manual](http://www.lua.org/manual/5.2/).
To create a Lua Page, make a file that is called `<something>.lp`. For example,
let's say it is going to be `my_page.lp`. It is important to have a file
To create a Lua Page, make a file that is called `ANY_NAME.lp`. For example,
`my_page.lp`. It is important to have a file
name that ends up with `.lp`, cause this is the way mongoose recognises
Lua Page file. The contents of the file, just like
with PHP, is HTML with embedded Lua code. Lua code must be enclosed within
`<? ?>` blocks, and can appear anywhere on the page.
`&lt;? ?&gt;` blocks, and can appear anywhere on the page.
Mongoose does not send HTTP headers for Lua pages. Therefore,
every Lua Page must begin with HTTP status line and headers, like this:
@@ -27,26 +27,18 @@ Note that this example uses function `mg.write()`, which prints data to the
web page. Using function `mg.write()` is the way to generate web content from
inside Lua code. In addition to `mg.write()`, all standard library functions
are accessible from the Lua code (please check reference manual for details).
Information about the request is available in `mg.request_info` object.
Information about the request is available via the `mg.request_info` object.
I contains request method, all headers, etcetera. Please refer to
`struct mg_request_info` definition in
[mongoose.h](https://github.com/cesanta/mongoose/blob/master/mongoose.h)
to see what is available in `mg.request_info`. Also,
[page.lp](https://github.com/cesanta/mongoose/blob/master/test/page.lp) and
[prime_numbers.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/prime_numbers.lp)
contain some example code that uses `request_info` and other functions,
like form submission.
to see what is available via the `mg.request_info` object.
Check out [prime_numbers.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/prime_numbers.lp) for some example.
Mongoose exports the following to the Lua server page:
Mongoose exports the following to the Lua Server Page:
mg.write(str) -- writes string to the client
mg.onerror(msg) -- error handler, can be overridden
mg.request_info -- a table with request information
sqlite3 -- Sqlite3 interface
slite3 functions are documented at [lua.sqlite.org](http://lua.sqlite.org/),
and usage example is at
[page.lp](https://github.com/cesanta/mongoose/blob/master/test/page.lp).
Using Lua scripting it is easy to emulate SSI functionality. For example,
to include the content of another file, one can write:
@@ -56,4 +48,4 @@ to include the content of another file, one can write:
To serve a Lua Page, mongoose creates Lua context. That context is used for
all Lua blocks within the page. That means, all Lua blocks on the same page
share the same context. If one block defines a variable, for example, that
variable is visible in the block that follows.
variable is visible in all following blocks.
+44 -30
View File
@@ -10,8 +10,12 @@ this means to deny only that single IP address.
Subnet masks may vary from 0 to 32, inclusive. The default setting is to allow
all accesses. On each request the full list is traversed, and
the last match wins. Example: `$ mongoose -access_control_list -0.0.0.0/0,+192.168/16` to deny all acccesses except from 192.168/16 subnet. To learn
more about subnet masks, see the
the last match wins. Example: `$ mongoose -access_control_list -0.0.0.0/0,+192.168/16` to deny all acccesses except those from `192.168/16` subnet. Note that if the option is set, then all accesses are forbidden
by default. Thus in a previous example, `-0.0.0.0` part is not necessary.
For example, `$mongoose access_control_list +10.0.0.0/8`
means disallow all, allow subnet 10/8 only.
To learn more about subnet masks, see the
[Wikipedia page on Subnetwork](http://en.wikipedia.org/wiki/Subnetwork)
Default: not set, all accesses are allowed.
@@ -24,27 +28,32 @@ mongoose executable. Default: not set, no query logging is done.
Authorization realm used in `.htpasswd` authorization. Default: `mydomain.com`
### cgi_interpreter
Path to an executable to use as CGI interpreter for __all__ CGI scripts
regardless script extension.
for an interpreter. Default: not set, Mongoose looks at
Path to an executable to be used use as an interpreter for __all__ CGI scripts
regardless script extension. Default: not set, Mongoose looks at
[shebang line](http://en.wikipedia.org/wiki/Shebang_(Unix\).
For example, if both PHP and perl CGIs are used, then
`#!/path/to/php-cgi.exe` and `#!/path/to/perl.exe` must be first lines of the
respective CGI scripts. Note that paths should be either full file paths,
or file paths relative to the directory where mongoose server is located.
or file paths relative to the directory where mongoose executable is located.
If all CGIs use the same interpreter, for example they are all PHP, then
`cgi_interpreter` option can be set to the path to `php-cgi.exe` executable and
shebang line in the CGI scripts can be omitted.
Note that PHP scripts must use `php-cgi.exe` executable, not `php.exe`.
**Note**: PHP scripts must use `php-cgi.exe`, not `php.exe`.
### cgi_pattern
All files that match `cgi_pattern` are treated as CGI files. Default pattern
allows CGI files be anywhere. To restrict CGIs to a certain directory,
use `/path/to/cgi-bin/**.cgi` as pattern. Note that full file path is
matched against the pattern, not the URI. Note: if `MONGOOSE_CGI` environment
variable is set, then Mongoose passes it to the CGI script.
use `/path/to/cgi-bin/**.cgi` as a pattern. Note that **full file path** is
matched against the pattern, not the URI.
When Mongoose starts CGI program, it creates new environment for it (in
contrast, usually child program inherits the environment from parent). Several
environment variables however are inherited from Mongoose's environment,
they are: `PATH`, `TMP`, `TEMP`, `TMPDIR`, `PERLLIB`, `MONGOOSE_CGI`. On UNIX
it is also `LD_LIBRARY_PATH`. On Windows it is also `COMSPEC`, `SYSTEMROOT`,
`SystemDrive`, `ProgramFiles`, `ProgramFiles(x86)`, `CommonProgramFiles(x86)`.
Default: `**.cgi$|**.pl$|**.php$`
@@ -60,9 +69,10 @@ A directory to serve. Default: current working directory.
### enable\_directory\_listing
Enable directory listing, either `yes` or `no`. Default: `yes`.
### error\_log\_file
Path to a file for error logs. Either full path, or relative to the
mongoose executable. Default: not set, no errors are logged.
### enable\_proxy
Enable proxy functionality, either `yes` or `no`. If set to `yes`, then
browsers can be configured to use Mongoose as a proxy. Default: `no`.
### extra\_mime\_types
Extra mime types to recognize, in form `extension1=type1,extension2=type2,...`.
@@ -85,29 +95,27 @@ must be for a file name only, not including directory name, e.g.
`mongoose -hide_files_patterns secret.txt|even_more_secret.txt`. Default:
not set.
### idle\_timeout\_ms
Timeout for idle connections. Default: 30000 (30 seconds)
### index_files
Comma-separated list of files to be treated as directory index
files. Default: `index.html,index.htm,index.cgi,index.shtml,index.php`
### ssl_certificate
Path to SSL certificate file. The file must be in PEM format,
and it must have both private key and certificate, see for example
[ssl_cert.pem](https://github.com/cesanta/mongoose/blob/master/build/ssl_cert.pem). If this option is set, then Mongoose serves SSL connections on
`listening_port`. Default: not set.
### listening_port
Port to listen on. Port could be prepended by the specific IP address to bind
to, e.g. `mongoose -listening_port 127.0.0.1:8080`. Otherwise Mongoose
will bind to all addresses. Default: 8080.
will bind to all addresses. To enable SSL, build Mongoose with
`-DNS_ENABLE_SSL` compilation option, and specify `listening_port` as
`ssl://PORT:SSL_CERTIFICATE.PEM`. Example SSL listener:
`mongoose -listening_port ssl://8043:ssl_cert.pem`. Note that PEM file should
be in PEM format, and must have both certificate and private key in it,
concatenated together. More than one listening port can be specified,
separated by comma,
for example `mongoose -listening_port 8080,8000`. Default: 8080.
### run\_as\_user
Switch to given user credentials after startup. UNIX-only. This option is
required when mongoose needs to bind on privileged port on UNIX, e.g.
$ sudo mongoose -listening_ports 80 -run_as_user nobody
$ sudo mongoose -listening_port 80 -run_as_user nobody
Default: not set.
@@ -116,17 +124,20 @@ Comma-separated list of URL rewrites in the form of
`uri_pattern=file_or_directory_path`. When Mongoose receives the request,
it constructs the file name to show by combining `document_root` and the URI.
However, if the rewrite option is used and `uri_pattern` matches the
requested URI, then `document_root` is ignored. Insted,
requested URI, then `document_root` is ignored. Instead,
`file_or_directory_path` is used, which should be a full path name or
a path relative to the web server's current working directory. Note that
`uri_pattern`, as all mongoose patterns, is a prefix pattern. If `uri_patters`
`uri_pattern`, as all mongoose patterns, is a prefix pattern. If `uri_pattern`
is a number, then it is treated as HTTP error code, and `file_or_directory_path`
should be an URI to redirect to. Mongoose will issue `302` temporary redirect
to the specified URI, appending two parameters:
`?code=<http_error_code&orig_uri=<original_uri>`.
to the specified URI with following parameters:
`?code=HTTP_ERROR_CODE&orig_uri=ORIGINAL_URI&query_string=QUERY_STRING`.
This makes it possible to serve many directories outside from `document_root`,
redirect all requests to scripts, and do other tricky things. Examples:
If `uri_pattern` starts with `@` symbol, then Mongoose compares
it with the `HOST` header of the request. If they are equal, Mongoose sets
document root to `file_or_directory_path`, implementing virtual hosts support.
Examples:
# Redirect all accesses to `.doc` files to a special script
mongoose -url_rewrites **.doc$=/path/to/cgi-bin/handle_doc.cgi
@@ -137,4 +148,7 @@ redirect all requests to scripts, and do other tricky things. Examples:
# Redirect 404 errors to a specific error page
mongoose -url_rewrites 404=/cgi-bin/error.cgi
# Virtual hosts example: serve foo.com domain from different directory
mongoose -url_rewrites @foo.com=/var/www/foo.com
Default: not set.
+49
View File
@@ -0,0 +1,49 @@
How To Create A PHP Website With Mongoose
===========================================
## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`:
![screenshot](http://cesanta.com/images/tut_php/tut1.png)
## 2. Inside `my_website` directory, create a new file called "index". This will be the default web page shown when the website is visited.
![screenshot](http://cesanta.com/images/tut_php/tut2.png)
## 3. Open index file with your favorite editor (for example, Notepad) and enter some HTML / PHP code:
![screenshot](http://cesanta.com/images/tut_php/tut3.png)
## 4. Save this file as `index.php`:
![screenshot](http://cesanta.com/images/tut_php/tut4.png)
## 5. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside `my_website` directory:
![screenshot](http://cesanta.com/images/tut_php/tut5.png)
## 6. Double-click mongoose executable. An icon will appear on a system tray in the bottom right corner of the desktop:
![screenshot](http://cesanta.com/images/tut_php/tut6.png)
## 7. Download PHP 5.3 zip (do NOT download PHP 5.5 cause you might have missing DLLs problem) from http://windows.php.net/download and extract it to `C:\php5` directory:
![screenshot](http://cesanta.com/images/tut_php/tut7.png)
## 8. Click on the mongoose icon and choose "Edit Settings" menu.:
![screenshot](http://cesanta.com/images/tut_php/tut8.png)
## 9. A settings dialog will appear. Click on `cgi_interpreter` button:
![screenshot](http://cesanta.com/images/tut_php/tut9.png)
## 10. Choose `C:\php5\php-cgi.exe` and click "Save Settings":
![screenshot](http://cesanta.com/images/tut_php/tut10.png)
## 11. Click on the mongoose icon and choose "Go to my address" menu:
![screenshot](http://cesanta.com/images/tut_php/tut11.png)
## 12. A browser will popup displaying `index.php`.
![screenshot](http://cesanta.com/images/tut_php/tut12.png)
+193
View File
@@ -0,0 +1,193 @@
# Mongoose Release Notes
## Release 5.5, October 28 2014
Changes in Libmongoose library:
- Added new API function: `mg_forward()` for proxying functionality
- Added new API function: `mg_send_file_data()` for sending file data
- Added new utility API functions: `mg_mmap() and mg_munmap()`
- Changed the way SSL settings are handled: removed `ssl_certificate` and
`ssl_ca_certificate` options, and instead made `listening_port` accept
`ssl://PORT:SSL_CERT:CA_CERT` notation
- Added ability to listen on multiple ports, see `listening_port` documentation
- Added `enable_proxy` option
- Added [cookie_authentication](https://github.com/cesanta/mongoose/tree/master/examples/cookie_authentication) example
- Added [websocket\_ssl\_proxy](https://github.com/cesanta/mongoose/tree/master/examples/websocket_ssl_proxy) example
- Added [http_client](https://github.com/cesanta/mongoose/tree/master/examples/http_client) example
- Increased default 'idle connection' timeout from 30 to 300 seconds
- Fixed MinGW build
- Refactored all examples, put each in it's own directory with dedicated build
- Many smaller bugfixed, including SSL, CGI, API, proxy, etc
Changes in pre-compiled binaries:
- Support for multiple listening ports
- Fixed CGI handling for scripts that specify interpreter in the hashbang line
## Release 5.4, July 28 2014
Changes in Libmongoose library:
- Added `hexdump_file` option for low-level request/reply debugging
- Added `mg_template()` API function for generating HTML pages from
templates with expansions
- Fixed `struct mg_connection::local_ip` handling, `mg_set_option()`
behavior with NULL values
- Added `mg_send_file()` call to send arbitrary file to the client
- Added `mg_terminate_ssl()` for SSL termination functionality
- Added HTTP proxy support, `enable_proxy` config option
- Added `mg_next()` for iterating over existing active connections
- Added client-side SSL auth, `ssl_ca_certificate` option
- Added `mg_wakeup_server_ex()` for pushing messages to existing connections
- Added `MG_WS_HANDSHAKE` and `MG_WS_CONNECT` events that are sent on
Websocket handshake is connection establishment, respectively
- Removed server-side Lua support
- Filesystem access, reading from socket/SSL performance improvements
- DAV PROPFIND memory leak fixed
- Added `big_upload.c` and enhanced `upload.c` example
- Added `proxy.c` example that demonstrates proxy functionality and SSE pushes
- Added `websocket2.c` example that shows simple web chat implementation
over websockets
- Various minor fixes
Changes in pre-compiled binaries:
- Created HTML administration console
- When server is started, browser is started automatically
- Fixed directory listing bug when directory contains `#` character
- Removed built-in Lua Server Pages in the binary, and instead
added Mongoose + Lua developer bundle which has Lua Server Pages support.
That also solves external Lua modules loading problem.
## Release 5.3, March 10 2014
Changes in Libmongoose library:
* Moved to the evented API. Updated API documentation is at
http://cesanta.com/docs/Embed.shtml
http://cesanta.com/docs/API.shtml
* Added `MG_LUA` event for exporting custom variables to the Lua environment
* Added virtual hosts capability, see `url_rewrites` option description at
http://cesanta.com/docs/Options.shtml
* Added mjpg serving example
* Cleaned up and documented HTTP client API, with unit tests
* Added `mg_wakeup_server()` to awaken `mg_poll_server()`
from another thread
* Moved Mongoose IO core to [https://github.com/cesanta/net_skeleton](Net Skeleton)
* Added connection hexdump functionality for developers
* Bug fixes
Changes in pre-compiled binaries:
* New awesome Mongoose logos by our designer Katrin - thanks Katrin!
Check them out at http://cesanta.com/products.shtml
* Added Lua Server Pages support to the free version, quick intro is at
http://cesanta.com/docs/Lua.shtml
* Added quick "Set shared directory" menu item to set `document_root`
* Added SSI support to the Pro version
* Removed SSL support from the Pro version
## Release 5.2, Feb 1 2014
* Windows binary made fully UNICODE aware. In previous versions,
the presence of non-ASCII chars in document root, CGI script name,
or directory name might have broken Mongoose as stand-alone
or as Windows service. Now Mongoose works with non-ASCII paths properly.
Internally, Mongoose uses UTF8 encoding. When making WinAPI calls,
mongoose converts UTF8 strings to wide chars and calls UNICODE API.
* Enhanced authorization API by providing `mg_set_auth_handler()` and
`mg_authorize_digest()`
* Removed `mg_add_uri_handler()`, added `mg_set_request_handler()`.
There is only oneURI handler that handles all requests, just like in 4.x.
The reason for this change is to provide an ability to catch all URIs,
and at the same time signal Mongoose to continue handling specific URIs.
* Added `mg_parse_multipart()` API for file uploads.
Note that the restriction on uploading huge files still exists,
and will be eliminated in the next release.
* Allowing mongoose to bind to port 0, in which case it'll bind to any
random unused port.
* Moved `idle_timeout_ms` run-time option to compile-time flag
* Added asynchronous HTTP client, not documented yet. Documentation and
examples are coming in the next couple of weeks. Async Websocket client
is scheduled for the next release. See usage examples at `unit_test.c`
* Windows and MacOS pre-built binaries are now split to free and paid ones,
paid binaries include CGI, SSL, Lua, Sqlite, support and updates.
Linux pre-built binary includes all functionality and is free, and will
continue to be free. Source code for Windows and MacOS GUI is closed.
Disclaimer: source code for the command line stand-alone server,
as well as Mongoose library itself, will never be closed.
* Multiple bug fixes and minor enhancements
## Release 5.1, Jan 10 2014
* CGI-related bugs where fixed, primarily for Windows platform
* Bugs on Windows related to UNICODE support were fixed
* Added a feature to support "error pages" through redirect.
Done using `-url_redirects` option, details are on
http://cesanta.com/docs/Options.shtml
## Release 5.0, Jan 6 2014
* Internal core has been changed from blocking, thread-per-connection to
non-blocking, asynchronous, one thread for all.
* API modification for server creation and response creation. That allowed
keep-alive support for dynamic requests, boosting the embedded performance
to 100+ thousands requests per second on a single core
(as measured on my development MacBook laptop)
* Unified handling of POST requests and Websocket requests by putting a
payload into `conn->content`, `conn->content_len` attributes.
That simplified user code and eliminated the need of `mg_read()`,
since mongoose buffers all data prior to calling the callback
* keep-alive support is the default
* Dropped SSI support and throttling support
* Several configuraition parameters are gone:
* `cgi_environment` (replaced with MONGOOSE_CGI),
* `protect_uri` (not useful)
* `ssi_pattern` (SSI support is gone)
* `throttle` (throttling support is gone)
* `error_log_file` (not used)
* `enable_keep_alive` (enabled by default)
* `listening_ports` (renamed to listening_port)
* `num_threads` (core has changed to single thread)
* `put_delete_auth_file` (renamed to dav_auth_file)
* `authentication_domain` (renamed to auth_domain)
* Due to the async, non-blocking nature of the core, few restrictions
are now in place:
* user callbacks must not block
* POST and Websocket data are now buffered, and cannot be huge
* mongoose is now capable on listening on only one port
## Release 4.1, Oct 2013
## Release 4.0, Oct 2013
## Release 3.8, Sep 2013
## Release 3.7, Feb 2 2013
* Added "redirect to SSL port" functionality, e.g. if you specify
`-listening_ports 8080r,8043s`
then all requests to HTTP port 8080 will be redirected to HTTPS port 8043
* Added `mg_download()` API, an HTTP client interface!
* Lua server pages now must output HTTP headers -- full control for Lua
* Added pre-built binary for MacOS, with initial GUI support
* API change: got rid of events, moved to struct `mg_callbacks`
* Bugfixes, thanks to contributors
## Release 3.7, Jan 18 2013
* Fixed source code archive (main.c was missing)
* Extended Windows GUI functionality:
* Added "Start browser" systray popup menu item
* Enhanced configuration editor
* Renamed config options:
* `put_delete_passwords_file` -> `put_delete_auth_file`
* `global_passwords_file` -> `global_auth_file`
* `select()` changed to `poll()`, to avoid big file descriptor
`FD_SET` problem on UNIX
* Couple of bugfixes, thanks to contributors
Eearlier release notes could be found by searching
[Mongoose mailing list](https://groups.google.com/forum/#!forum/mongoose-users)
+11 -10
View File
@@ -2,13 +2,13 @@
Mongoose is small and easy to use web server built on top of
mongoose library. It is designed with maximum simplicity in mind. For example,
to share any directory, just drop mongoose executable file in that directory,
to share any directory, just drop mongoose executable in that directory,
double-click it (on UNIX, run it from shell) and launch a browser at
[http://localhost:8080](http://localhost:8080) Note that 'localhost' should
be changed to a machine's name if a folder is accessed from other computer.
On Windows and Mac, Mongoose iconifies itself to the system tray when started.
Right-click on the icon pops up a menu, where it is possible to stop
Right-click on the icon to pop up a menu, where it is possible to stop
mongoose, or configure it.
On UNIX, `mongoose` is a command line utility. Running `mongoose` in
@@ -68,17 +68,18 @@ recognized by the file name pattern. Mongoose uses shell-like glob
patterns. Pattern match starts at the beginning of the string, so essentially
patterns are prefix patterns. Syntax is as follows:
** Matches everything
* Matches everything but slash character, '/'
? Matches any character
$ Matches the end of the string
| Matches if pattern on the left side or the right side matches.
** Matches everything
* Matches everything but slash character, '/'
? Matches any character
$ Matches the end of the string
| Matches if pattern on the left side or the right side matches.
All other characters in the pattern match themselves. Examples:
**.cgi$ Any string that ends with .cgi
/foo Any string that begins with /foo
**a$|**b$ Any string that ends with a or b
# Pattern Meaning
**.cgi$ Any string that ends with .cgi
/foo Any string that begins with /foo
**a$|**b$ Any string that ends with a or b
To restrict CGI files only to `/cgi-bin/` directory, use this setting:
+9 -48
View File
@@ -1,54 +1,15 @@
CFLAGS = -W -Wall -I.. -pthread -g -pipe $(CFLAGS_EXTRA)
DLL_FLAGS = -DLUA_COMPAT_ALL -I../build
RM = rm -rf
# Copyright (c) 2014 Cesanta Software
# All rights reserved
ifeq ($(OS),Windows_NT)
RM = del /q /f
else
UNAME_S := $(shell uname -s)
DLL_FLAGS += -shared
SUBDIRS = $(sort $(filter-out csharp/, $(dir $(wildcard */))))
X = $(SUBDIRS)
ifeq ($(UNAME_S),Linux)
CFLAGS += -ldl
endif
.PHONY: $(SUBDIRS)
ifeq ($(UNAME_S),Darwin)
# DLL_FLAGS += -bundle -undefined dynamic_lookup -dynamiclib
DLL_FLAGS += -flat_namespace -undefined suppress -dynamiclib
endif
endif
all: $(SUBDIRS)
all: websocket_html.c
$(CC) hello.c ../mongoose.c -o hello $(CFLAGS)
$(CC) websocket.c websocket_html.c ../mongoose.c -o websocket $(CFLAGS)
$(CC) post.c ../mongoose.c -o post $(CFLAGS)
$(CC) multi_threaded.c ../mongoose.c -o multi_threaded $(CFLAGS)
$(CC) upload.c ../mongoose.c -o upload $(CFLAGS)
$(CC) auth.c ../mongoose.c -o auth $(CFLAGS)
$(CC) server.c ../mongoose.c -o server $(CFLAGS)
# $(CC) chat.c ../mongoose.c -o chat $(CFLAGS)
# $(CC) lua_dll.c ../build/lua_5.2.1.c -o $@.so $(CFLAGS) $(DLL_FLAGS)
websocket_html.c: websocket.html
perl mkdata.pl $< > $@
MSVC = ../../vc6
CL = $(MSVC)/bin/cl
CLFLAGS = /MD /TC /nologo $(DBG) /W3 \
/I$(MSVC)/include /I.. /Dsnprintf=_snprintf
LFLAGS = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86
windows: websocket_html.c
$(CL) hello.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
$(CL) websocket.c websocket_html.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
$(CL) post.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
$(CL) multi_threaded.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
$(CL) upload.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
$(CL) auth.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
$(CL) server.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
#$(CL) lua_dll.c $(CLFLAGS) $(DLL_FLAGS) /DLL $(LFLAGS) /SUBSYSTEM:WINDOWS /ENTRY:luaopen_lua_dll /EXPORT:luaopen_lua_dll /out:lua_dll.dll
$(SUBDIRS):
@$(MAKE) -C $@
clean:
-@$(RM) hello upload post websocket chat *.exe *.dSYM *.obj .*o
for d in $(SUBDIRS) ; do $(MAKE) -C $$d clean ; done
-32
View File
@@ -1,32 +0,0 @@
#include <stdio.h>
#include <string.h>
#include "mongoose.h"
static int auth_handler(struct mg_connection *conn) {
int result = MG_AUTH_FAIL; // Not authorized
FILE *fp;
// To populate passwords file, do
// mongoose -A my_passwords.txt mydomain.com admin admin
if ((fp = fopen("my_passwords.txt", "r")) != NULL) {
result = mg_authorize_digest(conn, fp);
fclose(fp);
}
return result;
}
int main(void) {
struct mg_server *server = mg_create_server(NULL);
mg_set_option(server, "listening_port", "8080");
mg_set_option(server, "document_root", ".");
mg_set_auth_handler(server, auth_handler);
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 1000);
}
mg_destroy_server(&server);
return 0;
}
+12
View File
@@ -0,0 +1,12 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = big_upload
CFLAGS = -W -Wall -pthread -I../.. -g -O0 $(CFLAGS_EXTRA)
SOURCES = $(PROG).c ../../mongoose.c
$(PROG): $(SOURCES)
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
+84
View File
@@ -0,0 +1,84 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mongoose.h"
static int handle_request(struct mg_connection *conn) {
if (strcmp(conn->uri, "/upload") == 0) {
FILE *fp = (FILE *) conn->connection_param;
if (fp != NULL) {
fwrite(conn->content, 1, conn->content_len, fp); // Write last bits
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
"Written %ld of POST data to a temp file:\n\n",
(long) ftell(fp));
// Temp file will be destroyed after fclose(), do something with the
// data here -- for example, parse it and extract uploaded files.
// As an example, we just echo the whole POST buffer back to the client.
rewind(fp);
mg_send_file_data(conn, fileno(fp));
return MG_MORE; // Tell Mongoose reply is not completed yet
} else {
mg_printf_data(conn, "%s", "Had no data to write...");
return MG_TRUE; // Tell Mongoose we're done with this request
}
} else {
mg_printf_data(conn, "%s",
"<html><body>Upload example."
"<form method=\"POST\" action=\"/upload\" "
" enctype=\"multipart/form-data\">"
"<input type=\"file\" name=\"file\" /> <br/>"
"<input type=\"submit\" value=\"Upload\" />"
"</form></body></html>");
return MG_TRUE; // Tell mongoose to close this connection
}
}
// Mongoose sends MG_RECV for every received POST chunk.
// When last POST chunk is received, Mongoose sends MG_REQUEST, then MG_CLOSE.
static int handle_recv(struct mg_connection *conn) {
FILE *fp = (FILE *) conn->connection_param;
// Open temporary file where we going to write data
if (fp == NULL && ((conn->connection_param = fp = tmpfile())) == NULL) {
return -1; // Close connection on error
}
// Return number of bytes written to a temporary file: that is how many
// bytes we want to discard from the receive buffer
return fwrite(conn->content, 1, conn->content_len, fp);
}
// Make sure we free all allocated resources
static int handle_close(struct mg_connection *conn) {
if (conn->connection_param != NULL) {
fclose((FILE *) conn->connection_param);
conn->connection_param = NULL;
}
return MG_TRUE;
}
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
switch (ev) {
case MG_AUTH: return MG_TRUE;
case MG_REQUEST: return handle_request(conn);
case MG_RECV: return handle_recv(conn);
case MG_CLOSE: return handle_close(conn);
default: return MG_FALSE;
}
}
int main(void) {
struct mg_server *server = mg_create_server(NULL, ev_handler);
mg_set_option(server, "listening_port", "8080");
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 1000);
}
mg_destroy_server(&server);
return 0;
}
-37
View File
@@ -1,37 +0,0 @@
// This file is part of mongoose web server project,
// https://github.com/cesanta/mongoose
using System;
using System.Runtime.InteropServices;
public class Program {
static private int EventHandler(MongooseEvent ev) {
if (ev.type != 1) {
return 0; // Mark as unhandled
}
MongooseRequestInfo request_info = (MongooseRequestInfo)
Marshal.PtrToStructure(ev.request_info, typeof(MongooseRequestInfo));
if (request_info.uri != "/test") {
return 0; // Mark as unhandled
}
Mongoose.write(ev.conn, "HTTP/1.1 200 OK\r\n\r\n");
Mongoose.write(ev.conn, "Hello from C#!\n");
return 1; // Mark as handled
}
static void Main() {
Mongoose web_server = new Mongoose(".", "9000",
new MongooseEventHandler(EventHandler));
Console.WriteLine("Mongoose v." + web_server.version_ + " started.");
Console.WriteLine("Press enter to exit program.");
// Serve requests until user presses "enter" on a keyboard
Console.ReadLine();
web_server.stop();
}
}
-93
View File
@@ -1,93 +0,0 @@
// This file is part of mongoose web server project,
// https://github.com/cesanta/mongoose
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)] public struct MongooseHeader {
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr name;
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr value;
};
// mongoose.h :: struct mg_request_info
[StructLayout(LayoutKind.Sequential)] public struct MongooseRequestInfo {
[MarshalAs(UnmanagedType.LPTStr)] public string request_method;
[MarshalAs(UnmanagedType.LPTStr)] public string uri;
[MarshalAs(UnmanagedType.LPTStr)] public string http_version;
[MarshalAs(UnmanagedType.LPTStr)] public string query_string;
[MarshalAs(UnmanagedType.LPTStr)] public string remote_user;
public int remote_ip;
public int remote_port;
public int is_ssl;
[MarshalAs(UnmanagedType.ByValArray,SizeConst=64)]
public MongooseHeader[] http_headers;
};
[StructLayout(LayoutKind.Sequential)] public struct MongooseEvent {
public int type;
public IntPtr user_data;
public IntPtr conn_data;
public IntPtr event_param;
public IntPtr conn;
public IntPtr request_info;
};
public delegate int MongooseEventHandlerN(ref MongooseEvent ev);
public delegate int MongooseEventHandler(MongooseEvent ev);
public class Mongoose {
public const string dll_name_ = "mongoose";
public string version_ = "??";
// These are here to store a ref to the callbacks
// while they are over in unmanaged code, to prevent garbage collection.
private event MongooseEventHandlerN delegates;
private IntPtr ctx_;
[DllImport(dll_name_)] private static extern
IntPtr mg_start([MarshalAs(UnmanagedType.LPArray,
ArraySubType=UnmanagedType.LPTStr)] string[] options,
MongooseEventHandlerN callback,
IntPtr user_data);
[DllImport(dll_name_)] private static extern void mg_stop(IntPtr ctx);
[DllImport(dll_name_)] private static extern IntPtr mg_version();
[DllImport(dll_name_)] public static extern int mg_write(IntPtr conn,
string data, int length);
public Mongoose(string document_root,
string listening_ports,
MongooseEventHandler event_handler) {
version_ = Marshal.PtrToStringAnsi(mg_version());
string[] options = {
"document_root", document_root,
"listening_ports", listening_ports,
null
};
MongooseEventHandlerN cb = delegate(ref MongooseEvent ev) {
return event_handler(ev);
};
// Prevent garbage collection
delegates += cb;
ctx_ = mg_start(options, cb, IntPtr.Zero);
}
public static int write(IntPtr conn, string data) {
return mg_write(conn, data, data.Length);
}
public void stop() {
if (this.ctx_ != IntPtr.Zero) {
mg_stop(this.ctx_);
}
this.ctx_ = IntPtr.Zero;
}
~Mongoose() {
stop();
}
}
-386
View File
@@ -1,386 +0,0 @@
// This file is part of the Mongoose project, http://code.google.com/p/mongoose
// It implements an online chat server. For more details,
// see the documentation on the project web site.
// To test the application,
// 1. type "make" in the directory where this file lives
// 2. point your browser to http://127.0.0.1:8081
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>
#include <pthread.h>
#include "mongoose.h"
#define MAX_USER_LEN 20
#define MAX_MESSAGE_LEN 100
#define MAX_MESSAGES 5
#define MAX_SESSIONS 2
#define SESSION_TTL 120
static const char *authorize_url = "/authorize";
static const char *login_url = "/login.html";
static const char *ajax_reply_start =
"HTTP/1.1 200 OK\r\n"
"Cache: no-cache\r\n"
"Content-Type: application/x-javascript\r\n"
"\r\n";
// Describes single message sent to a chat. If user is empty (0 length),
// the message is then originated from the server itself.
struct message {
long id; // Message ID
char user[MAX_USER_LEN]; // User that have sent the message
char text[MAX_MESSAGE_LEN]; // Message text
time_t timestamp; // Message timestamp, UTC
};
// Describes web session.
struct session {
char session_id[33]; // Session ID, must be unique
char random[20]; // Random data used for extra user validation
char user[MAX_USER_LEN]; // Authenticated user
time_t expire; // Expiration timestamp, UTC
};
static struct message messages[MAX_MESSAGES]; // Ringbuffer for messages
static struct session sessions[MAX_SESSIONS]; // Current sessions
static long last_message_id;
// Protects messages, sessions, last_message_id
static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
// Get session object for the connection. Caller must hold the lock.
static struct session *get_session(const struct mg_connection *conn) {
int i;
const char *cookie = mg_get_header(conn, "Cookie");
char session_id[33];
time_t now = time(NULL);
mg_get_cookie(cookie, "session", session_id, sizeof(session_id));
for (i = 0; i < MAX_SESSIONS; i++) {
if (sessions[i].expire != 0 &&
sessions[i].expire > now &&
strcmp(sessions[i].session_id, session_id) == 0) {
break;
}
}
return i == MAX_SESSIONS ? NULL : &sessions[i];
}
static void get_qsvar(const struct mg_request_info *request_info,
const char *name, char *dst, size_t dst_len) {
const char *qs = request_info->query_string;
mg_get_var(qs, strlen(qs == NULL ? "" : qs), name, dst, dst_len);
}
// Get a get of messages with IDs greater than last_id and transform them
// into a JSON string. Return that string to the caller. The string is
// dynamically allocated, caller must free it. If there are no messages,
// NULL is returned.
static char *messages_to_json(long last_id) {
const struct message *message;
int max_msgs, len;
char buf[sizeof(messages)]; // Large enough to hold all messages
// Read-lock the ringbuffer. Loop over all messages, making a JSON string.
pthread_rwlock_rdlock(&rwlock);
len = 0;
max_msgs = sizeof(messages) / sizeof(messages[0]);
// If client is too far behind, return all messages.
if (last_message_id - last_id > max_msgs) {
last_id = last_message_id - max_msgs;
}
for (; last_id < last_message_id; last_id++) {
message = &messages[last_id % max_msgs];
if (message->timestamp == 0) {
break;
}
// buf is allocated on stack and hopefully is large enough to hold all
// messages (it may be too small if the ringbuffer is full and all
// messages are large. in this case asserts will trigger).
len += snprintf(buf + len, sizeof(buf) - len,
"{user: '%s', text: '%s', timestamp: %lu, id: %lu},",
message->user, message->text, message->timestamp, message->id);
assert(len > 0);
assert((size_t) len < sizeof(buf));
}
pthread_rwlock_unlock(&rwlock);
return len == 0 ? NULL : strdup(buf);
}
// If "callback" param is present in query string, this is JSONP call.
// Return 1 in this case, or 0 if "callback" is not specified.
// Wrap an output in Javascript function call.
static int handle_jsonp(struct mg_connection *conn,
const struct mg_request_info *request_info) {
char cb[64];
get_qsvar(request_info, "callback", cb, sizeof(cb));
if (cb[0] != '\0') {
mg_printf(conn, "%s(", cb);
}
return cb[0] == '\0' ? 0 : 1;
}
// A handler for the /ajax/get_messages endpoint.
// Return a list of messages with ID greater than requested.
static void ajax_get_messages(struct mg_connection *conn,
const struct mg_request_info *request_info) {
char last_id[32], *json;
int is_jsonp;
mg_printf(conn, "%s", ajax_reply_start);
is_jsonp = handle_jsonp(conn, request_info);
get_qsvar(request_info, "last_id", last_id, sizeof(last_id));
if ((json = messages_to_json(strtoul(last_id, NULL, 10))) != NULL) {
mg_printf(conn, "[%s]", json);
free(json);
}
if (is_jsonp) {
mg_printf(conn, "%s", ")");
}
}
// Allocate new message. Caller must hold the lock.
static struct message *new_message(void) {
static int size = sizeof(messages) / sizeof(messages[0]);
struct message *message = &messages[last_message_id % size];
message->id = last_message_id++;
message->timestamp = time(0);
return message;
}
static void my_strlcpy(char *dst, const char *src, size_t len) {
strncpy(dst, src, len);
dst[len - 1] = '\0';
}
// A handler for the /ajax/send_message endpoint.
static void ajax_send_message(struct mg_connection *conn,
const struct mg_request_info *request_info) {
struct message *message;
struct session *session;
char text[sizeof(message->text) - 1];
int is_jsonp;
mg_printf(conn, "%s", ajax_reply_start);
is_jsonp = handle_jsonp(conn, request_info);
get_qsvar(request_info, "text", text, sizeof(text));
if (text[0] != '\0') {
// We have a message to store. Write-lock the ringbuffer,
// grab the next message and copy data into it.
pthread_rwlock_wrlock(&rwlock);
message = new_message();
// TODO(lsm): JSON-encode all text strings
session = get_session(conn);
assert(session != NULL);
my_strlcpy(message->text, text, sizeof(text));
my_strlcpy(message->user, session->user, sizeof(message->user));
pthread_rwlock_unlock(&rwlock);
}
mg_printf(conn, "%s", text[0] == '\0' ? "false" : "true");
if (is_jsonp) {
mg_printf(conn, "%s", ")");
}
}
// Redirect user to the login form. In the cookie, store the original URL
// we came from, so that after the authorization we could redirect back.
static void redirect_to_login(struct mg_connection *conn,
const struct mg_request_info *request_info) {
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
"Set-Cookie: original_url=%s\r\n"
"Location: %s\r\n\r\n",
request_info->uri, login_url);
}
// Return 1 if username/password is allowed, 0 otherwise.
static int check_password(const char *user, const char *password) {
// In production environment we should ask an authentication system
// to authenticate the user.
// Here however we do trivial check that user and password are not empty
return (user[0] && password[0]);
}
// Allocate new session object
static struct session *new_session(void) {
int i;
time_t now = time(NULL);
pthread_rwlock_wrlock(&rwlock);
for (i = 0; i < MAX_SESSIONS; i++) {
if (sessions[i].expire == 0 || sessions[i].expire < now) {
sessions[i].expire = time(0) + SESSION_TTL;
break;
}
}
pthread_rwlock_unlock(&rwlock);
return i == MAX_SESSIONS ? NULL : &sessions[i];
}
// Generate session ID. buf must be 33 bytes in size.
// Note that it is easy to steal session cookies by sniffing traffic.
// This is why all communication must be SSL-ed.
static void generate_session_id(char *buf, const char *random,
const char *user) {
mg_md5(buf, random, user, NULL);
}
static void send_server_message(const char *fmt, ...) {
va_list ap;
struct message *message;
pthread_rwlock_wrlock(&rwlock);
message = new_message();
message->user[0] = '\0'; // Empty user indicates server message
va_start(ap, fmt);
vsnprintf(message->text, sizeof(message->text), fmt, ap);
va_end(ap);
pthread_rwlock_unlock(&rwlock);
}
// A handler for the /authorize endpoint.
// Login page form sends user name and password to this endpoint.
static void authorize(struct mg_connection *conn,
const struct mg_request_info *request_info) {
char user[MAX_USER_LEN], password[MAX_USER_LEN];
struct session *session;
// Fetch user name and password.
get_qsvar(request_info, "user", user, sizeof(user));
get_qsvar(request_info, "password", password, sizeof(password));
if (check_password(user, password) && (session = new_session()) != NULL) {
// Authentication success:
// 1. create new session
// 2. set session ID token in the cookie
// 3. remove original_url from the cookie - not needed anymore
// 4. redirect client back to the original URL
//
// The most secure way is to stay HTTPS all the time. However, just to
// show the technique, we redirect to HTTP after the successful
// authentication. The danger of doing this is that session cookie can
// be stolen and an attacker may impersonate the user.
// Secure application must use HTTPS all the time.
my_strlcpy(session->user, user, sizeof(session->user));
snprintf(session->random, sizeof(session->random), "%d", rand());
generate_session_id(session->session_id, session->random, session->user);
send_server_message("<%s> joined", session->user);
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
"Set-Cookie: session=%s; max-age=3600; http-only\r\n" // Session ID
"Set-Cookie: user=%s\r\n" // Set user, needed by Javascript code
"Set-Cookie: original_url=/; max-age=0\r\n" // Delete original_url
"Location: /\r\n\r\n",
session->session_id, session->user);
} else {
// Authentication failure, redirect to login.
redirect_to_login(conn, request_info);
}
}
// Return 1 if request is authorized, 0 otherwise.
static int is_authorized(const struct mg_connection *conn,
const struct mg_request_info *request_info) {
struct session *session;
char valid_id[33];
int authorized = 0;
// Always authorize accesses to login page and to authorize URI
if (!strcmp(request_info->uri, login_url) ||
!strcmp(request_info->uri, authorize_url)) {
return 1;
}
pthread_rwlock_rdlock(&rwlock);
if ((session = get_session(conn)) != NULL) {
generate_session_id(valid_id, session->random, session->user);
if (strcmp(valid_id, session->session_id) == 0) {
session->expire = time(0) + SESSION_TTL;
authorized = 1;
}
}
pthread_rwlock_unlock(&rwlock);
return authorized;
}
static void redirect_to_ssl(struct mg_connection *conn,
const struct mg_request_info *request_info) {
const char *p, *host = mg_get_header(conn, "Host");
if (host != NULL && (p = strchr(host, ':')) != NULL) {
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
"Location: https://%.*s:8082/%s:8082\r\n\r\n",
(int) (p - host), host, request_info->uri);
} else {
mg_printf(conn, "%s", "HTTP/1.1 500 Error\r\n\r\nHost: header is not set");
}
}
static int event_handler(struct mg_event *event) {
struct mg_request_info *request_info = event->request_info;
struct mg_connection *conn = event->conn;
int result = 1;
if (event->type != MG_REQUEST_BEGIN) return 0;
if (!request_info->is_ssl) {
redirect_to_ssl(conn, request_info);
} else if (!is_authorized(conn, request_info)) {
redirect_to_login(conn, request_info);
} else if (strcmp(request_info->uri, authorize_url) == 0) {
authorize(conn, request_info);
} else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) {
ajax_get_messages(conn, request_info);
} else if (strcmp(request_info->uri, "/ajax/send_message") == 0) {
ajax_send_message(conn, request_info);
} else {
// No suitable handler found, mark as not processed. Mongoose will
// try to serve the request.
result = 0;
}
return result;
}
static const char *options[] = {
"document_root", "html",
"listening_ports", "8081,8082s",
"ssl_certificate", "ssl_cert.pem",
"num_threads", "5",
NULL
};
int main(void) {
struct mg_context *ctx;
// Initialize random number generator. It will be used later on for
// the session identifier creation.
srand((unsigned) time(0));
// Setup and start Mongoose
if ((ctx = mg_start(options, event_handler, NULL)) == NULL) {
printf("%s\n", "Cannot start chat server, fatal exit");
exit(EXIT_FAILURE);
}
// Wait until enter is pressed, then exit
printf("Chat server started on ports %s, press enter to quit.\n",
mg_get_option(ctx, "listening_ports"));
getchar();
mg_stop(ctx);
printf("%s\n", "Chat server stopped.");
return EXIT_SUCCESS;
}
// vim:ts=2:sw=2:et
+12
View File
@@ -0,0 +1,12 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = cookie_auth
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
SOURCES = $(PROG).c ../../mongoose.c
$(PROG): $(SOURCES)
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
@@ -0,0 +1,96 @@
// Copyright (c) 2014 Cesanta Software
// All rights reserved
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "mongoose.h"
static const char *s_login_uri = "/login.html";
static const char *s_secret = ":-)"; // Must be known only to server
static void generate_ssid(const char *user_name, const char *expiration_date,
char *ssid, size_t ssid_size) {
char hash[33];
mg_md5(hash, user_name, ":", expiration_date, ":", s_secret, NULL);
snprintf(ssid, ssid_size, "%s|%s|%s", user_name, expiration_date, hash);
}
static int check_auth(struct mg_connection *conn) {
char ssid[100], calculated_ssid[100], name[100], expire[100];
// Always authenticate requests to login page
if (strcmp(conn->uri, s_login_uri) == 0) {
return MG_TRUE;
}
// Look for session ID in the Cookie.
// That session ID can be validated against the database that stores
// current active sessions.
mg_parse_header(mg_get_header(conn, "Cookie"), "ssid", ssid, sizeof(ssid));
if (sscanf(ssid, "%[^|]|%[^|]|", name, expire) == 2) {
generate_ssid(name, expire, calculated_ssid, sizeof(calculated_ssid));
if (strcmp(ssid, calculated_ssid) == 0) {
return MG_TRUE; // Authenticate
}
}
// Auth failed, do NOT authenticate, redirect to login page
mg_printf(conn, "HTTP/1.1 302 Moved\r\nLocation: %s\r\n\r\n", s_login_uri);
return MG_FALSE;
}
static int check_login_form_submission(struct mg_connection *conn) {
char name[100], password[100], ssid[100], expire[100], expire_epoch[100];
mg_get_var(conn, "name", name, sizeof(name));
mg_get_var(conn, "password", password, sizeof(password));
// A real authentication mechanism should be employed here.
// Also, the whole site should be served through HTTPS.
if (strcmp(name, "Joe") == 0 && strcmp(password, "Doe") == 0) {
// Generate expiry date
time_t t = time(NULL) + 3600; // Valid for 1 hour
snprintf(expire_epoch, sizeof(expire_epoch), "%lu", (unsigned long) t);
strftime(expire, sizeof(expire), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t));
generate_ssid(name, expire_epoch, ssid, sizeof(ssid));
// Set "session id" cookie, there could be some data encoded in it.
mg_printf(conn,
"HTTP/1.1 302 Moved\r\n"
"Set-Cookie: ssid=%s; expire=\"%s\"; http-only; HttpOnly;\r\n"
"Location: /\r\n\r\n",
ssid, expire);
return MG_TRUE;
}
return MG_FALSE;
}
static int serve_request(struct mg_connection *conn) {
if (strcmp(conn->uri, s_login_uri) == 0 &&
strcmp(conn->request_method, "POST") == 0) {
return check_login_form_submission(conn);
}
return MG_FALSE; // Serve files in the document_root
}
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
switch (ev) {
case MG_AUTH: return check_auth(conn);
case MG_REQUEST: return serve_request(conn);
default: return MG_FALSE;
}
}
int main(void) {
struct mg_server *server = mg_create_server(NULL, ev_handler);
mg_set_option(server, "listening_port", "8080");
mg_set_option(server, "document_root", ".");
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 1000);
}
mg_destroy_server(&server);
return 0;
}
+33
View File
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>WebSocket Test</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
body {
background-color: #cde; margin: 0;
padding: 0; font: 14px Helvetica, Arial, sans-serif;
}
* { outline: none; }
div.content {
width: 800px; margin: 2em auto; padding: 20px 50px;
background-color: #fff; border-radius: 1em;
}
label { display: inline-block; min-width: 7em; }
input { border: 1px solid #ccc; padding: 0.4em; margin: 0 0 10px 0; }
a:link, a:visited { color: #69c; text-decoration: none; }
@media (max-width: 700px) {
body { background-color: #fff; }
div.content {
width: auto; margin: 0 auto; border-radius: 0; padding: 1em;
}
}
</style>
<body>
<div class="content">
<h1>Mongoose Cookie Base Authentication</h1>
<p>This is an index page. Authentication succeeded.</p>
</body>
</html>
+44
View File
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>WebSocket Test</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
body {
background-color: #cde; margin: 0;
padding: 0; font: 14px Helvetica, Arial, sans-serif;
}
* { outline: none; }
div.content {
width: 800px; margin: 2em auto; padding: 20px 50px;
background-color: #fff; border-radius: 1em;
}
label { display: inline-block; min-width: 7em; }
input { border: 1px solid #ccc; padding: 0.4em; margin: 0 0 10px 0; }
a:link, a:visited { color: #69c; text-decoration: none; }
@media (max-width: 700px) {
body { background-color: #fff; }
div.content {
width: auto; margin: 0 auto; border-radius: 0; padding: 1em;
}
}
</style>
<body>
<div class="content">
<h1>Mongoose Cookie Based Authentication</h1>
<p>Use name "Joe", password "Doe" to login.</p>
<form method="POST">
<div>
<label>Name:</label>
<input type="text" name="name"/>
</div><div>
<label>Password:</label>
<input type="password" name="password"/>
</div><div>
<input type="submit" value="Login"/>
</div>
</form>
</body>
</html>
+43
View File
@@ -0,0 +1,43 @@
// This file is part of mongoose web server project,
// https://github.com/cesanta/mongoose
using System;
public class Program {
static private int EventHandler(IntPtr conn_ptr, int ev) {
MongooseConnection conn = (MongooseConnection)
System.Runtime.InteropServices.Marshal.PtrToStructure(
conn_ptr , typeof(MongooseConnection));
if (ev == 102) {
// MG_AUTH
return 1;
} else if (ev == 103) {
// MG_REQUEST
Mongoose.send_data(conn_ptr, "Hello from C#!\n");
Mongoose.send_data(conn_ptr, "URI: " + conn.uri + "\n");
Mongoose.send_data(conn_ptr, "HTTP Headers:\n");
for (int i = 0; i < conn.num_headers; i++) {
IntPtr name = conn.http_headers[i].name;
IntPtr val = conn.http_headers[i].value;
System.Runtime.InteropServices.Marshal.PtrToStringAnsi(name);
Mongoose.send_data(conn_ptr, " " +
System.Runtime.InteropServices.Marshal.PtrToStringAnsi(name) + ": " +
System.Runtime.InteropServices.Marshal.PtrToStringAnsi(val) + "\n");
}
return 1;
}
return 0;
}
static void Main() {
Mongoose web_server = new Mongoose(".", "9001",
new MongooseEventHandler(EventHandler));
Console.WriteLine("Mongoose started, press Ctrl-C to exit.");
for (;;) {
web_server.poll(1000);
}
}
}
+68
View File
@@ -0,0 +1,68 @@
// This file is part of mongoose web server project,
// https://github.com/cesanta/mongoose
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)] public struct MongooseHeader {
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr name;
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr value;
};
// mongoose.h :: struct mg_connection
[StructLayout(LayoutKind.Sequential)] public struct MongooseConnection {
[MarshalAs(UnmanagedType.LPTStr)] public string request_method;
[MarshalAs(UnmanagedType.LPTStr)] public string uri;
[MarshalAs(UnmanagedType.LPTStr)] public string http_version;
[MarshalAs(UnmanagedType.LPTStr)] public string query_string;
[MarshalAs(UnmanagedType.ByValArray,SizeConst=48)] public char[] remote_ip;
[MarshalAs(UnmanagedType.LPTStr)] public string local_ip;
[MarshalAs(UnmanagedType.U2)] public short remote_port;
[MarshalAs(UnmanagedType.U2)] public short local_port;
[MarshalAs(UnmanagedType.SysInt)] public int num_headers;
[MarshalAs(UnmanagedType.ByValArray,SizeConst=30)]
public MongooseHeader[] http_headers;
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr content;
[MarshalAs(UnmanagedType.SysInt)] public int content_len;
[MarshalAs(UnmanagedType.SysInt)] public int is_websocket;
[MarshalAs(UnmanagedType.SysInt)] public int status_code;
[MarshalAs(UnmanagedType.SysInt)] public int wsbits;
};
public delegate int MongooseEventHandler(IntPtr c, int ev);
public class Mongoose {
public const string dll_ = "mongoose";
private IntPtr server_;
[DllImport(dll_)] private static extern IntPtr
mg_create_server(IntPtr user_data, MongooseEventHandler eh);
[DllImport(dll_)] private static extern int
mg_poll_server(IntPtr server, int milli);
[DllImport(dll_)] private static extern IntPtr
mg_set_option(IntPtr server, string name, string value);
[DllImport(dll_)] public static extern int
mg_send_data(IntPtr conn, string data, int length);
public Mongoose(string document_root,
string listening_port,
MongooseEventHandler event_handler) {
server_ = mg_create_server(IntPtr.Zero, event_handler);
mg_set_option(server_, "document_root", document_root);
mg_set_option(server_, "listening_port", listening_port);
}
public static int send_data(IntPtr conn, string data) {
return mg_send_data(conn, data, data.Length);
}
public void poll(int milli) {
mg_poll_server(server_, milli);
}
// TODO: add destructor and call mg_destroy_server()
}
+12
View File
@@ -0,0 +1,12 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = digest_auth
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
SOURCES = $(PROG).c ../../mongoose.c
$(PROG): $(SOURCES)
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
@@ -0,0 +1,36 @@
#include <stdio.h>
#include <string.h>
#include "mongoose.h"
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
if (ev == MG_AUTH) {
int result = MG_FALSE; // Not authorized
FILE *fp;
// To populate passwords file, do
// mongoose -A my_passwords.txt mydomain.com admin admin
if ((fp = fopen("my_passwords.txt", "r")) != NULL) {
result = mg_authorize_digest(conn, fp);
fclose(fp);
}
return result;
}
return MG_FALSE;
}
int main(void) {
struct mg_server *server = mg_create_server(NULL, ev_handler);
mg_set_option(server, "listening_port", "8080");
mg_set_option(server, "document_root", ".");
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 1000);
}
mg_destroy_server(&server);
return 0;
}
-47
View File
@@ -1,47 +0,0 @@
#include <stdio.h>
#include <string.h>
#include "mongoose.h"
static int index_html(struct mg_connection *conn) {
FILE *fp = (FILE *) conn->connection_param;
char buf[200];
size_t n = 0;
if (fp == NULL) {
fp = fopen("../mongoose.c", "r");
conn->connection_param = (void *) fp;
}
if (fp != NULL) {
n = fread(buf, 1, sizeof(buf), fp);
mg_send_data(conn, buf, n);
if (n < sizeof(buf) || conn->wsbits != 0) {
fclose(fp);
conn->connection_param = NULL;
}
}
return n < sizeof(buf) ? 1 : 0;
}
int main(void) {
struct mg_server *server;
// Create and configure the server
server = mg_create_server(NULL);
mg_set_option(server, "listening_port", "8080");
mg_add_uri_handler(server, "/", index_html);
// Serve request. Hit Ctrl-C to terminate the program
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 1000);
}
// Cleanup, and free server instance
mg_destroy_server(&server);
return 0;
}
+20
View File
@@ -0,0 +1,20 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = file_upload
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
SOURCES = $(PROG).c ../../mongoose.c
all: $(PROG)
run: $(PROG)
./$(PROG)
$(PROG): $(SOURCES) Makefile
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
win:
wine cl $(SOURCES) /MD /nologo /DNDEBUG /O1 /I../.. /Fe$(PROG).exe
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib *.gc*
@@ -5,9 +5,9 @@
#include <string.h>
#include "mongoose.h"
static int index_html(struct mg_connection *conn) {
static int send_index_page(struct mg_connection *conn) {
const char *data;
int data_len;
int data_len, ofs = 0;
char var_name[100], file_name[100];
mg_printf_data(conn, "%s",
@@ -18,28 +18,33 @@ static int index_html(struct mg_connection *conn) {
"<input type=\"submit\" value=\"Upload\" />"
"</form>");
if (mg_parse_multipart(conn->content, conn->content_len,
var_name, sizeof(var_name),
file_name, sizeof(file_name),
&data, &data_len) > 0) {
mg_printf_data(conn, "%s", "Uploaded file:<pre>");
mg_send_data(conn, data, data_len);
mg_printf_data(conn, "%s", "/pre>");
while ((ofs = mg_parse_multipart(conn->content + ofs, conn->content_len - ofs,
var_name, sizeof(var_name),
file_name, sizeof(file_name),
&data, &data_len)) > 0) {
mg_printf_data(conn, "var: %s, file_name: %s, size: %d bytes<br>",
var_name, file_name, data_len);
}
mg_printf_data(conn, "%s", "</body></html>");
return MG_REQUEST_PROCESSED;
return MG_TRUE;
}
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
switch (ev) {
case MG_AUTH: return MG_TRUE;
case MG_REQUEST: return send_index_page(conn);
default: return MG_FALSE;
}
}
int main(void) {
struct mg_server *server;
// Create and configure the server
server = mg_create_server(NULL);
server = mg_create_server(NULL, ev_handler);
mg_set_option(server, "listening_port", "8080");
mg_set_request_handler(server, index_html);
// Serve request. Hit Ctrl-C to terminate the program
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
+12
View File
@@ -0,0 +1,12 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = form_submit
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
SOURCES = $(PROG).c ../../mongoose.c
$(PROG): $(SOURCES)
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
@@ -10,7 +10,7 @@ static const char *html_form =
"<input type=\"submit\" />"
"</form></body></html>";
static int handler(struct mg_connection *conn) {
static void send_reply(struct mg_connection *conn) {
char var1[500], var2[500];
if (strcmp(conn->uri, "/handle_post_request") == 0) {
@@ -33,18 +33,30 @@ static int handler(struct mg_connection *conn) {
// Show HTML form.
mg_send_data(conn, html_form, strlen(html_form));
}
}
return MG_REQUEST_PROCESSED;
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
if (ev == MG_REQUEST) {
send_reply(conn);
return MG_TRUE;
} else if (ev == MG_AUTH) {
return MG_TRUE;
} else {
return MG_FALSE;
}
}
int main(void) {
struct mg_server *server = mg_create_server(NULL);
struct mg_server *server = mg_create_server(NULL, ev_handler);
mg_set_option(server, "listening_port", "8080");
mg_set_request_handler(server, handler);
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 1000);
}
mg_destroy_server(&server);
return 0;
}
-29
View File
@@ -1,29 +0,0 @@
#include <stdio.h>
#include <string.h>
#include "mongoose.h"
// This function will be called by mongoose on every new request
static int index_html(struct mg_connection *conn) {
mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri);
return MG_REQUEST_PROCESSED;
}
int main(void) {
struct mg_server *server;
// Create and configure the server
server = mg_create_server(NULL);
mg_set_option(server, "listening_port", "8080");
mg_set_request_handler(server, index_html);
// Serve request. Hit Ctrl-C to terminate the program
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 1000);
}
// Cleanup, and free server instance
mg_destroy_server(&server);
return 0;
}
+21
View File
@@ -0,0 +1,21 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = hello_world
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
SOURCES = $(PROG).c ../../mongoose.c
all: $(PROG)
run: $(PROG)
./$(PROG)
$(PROG): $(SOURCES) Makefile
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
win:
wine cl $(SOURCES) /MD /nologo /DNDEBUG /O1 /I../.. /Fe$(PROG).exe
wine $(PROG).exe
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib *.gc*
+38
View File
@@ -0,0 +1,38 @@
// Copyright (c) 2014 Cesanta Software
// All rights reserved
//
// This example demostrates basic use of Mongoose embedded web server.
// $Date: 2014-09-09 22:20:23 UTC $
#include <stdio.h>
#include <string.h>
#include "mongoose.h"
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
switch (ev) {
case MG_AUTH: return MG_TRUE;
case MG_REQUEST:
mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri);
return MG_TRUE;
default: return MG_FALSE;
}
}
int main(void) {
struct mg_server *server;
// Create and configure the server
server = mg_create_server(NULL, ev_handler);
mg_set_option(server, "listening_port", "8080");
// Serve request. Hit Ctrl-C to terminate the program
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 1000);
}
// Cleanup, and free server instance
mg_destroy_server(&server);
return 0;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

-73
View File
@@ -1,73 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" dir="ltr">
<!-- This file is part of the Mongoose project,
http://code.google.com/p/mongoose -->
<head>
<title>Mongoose chat server</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<link type="text/css" rel="stylesheet" href="style.css"/>
<script src="jquery.js"></script>
<script src="main.js"></script>
</head>
<body>
<div id="header">
<div id="logo"></div>
<div class="rounded infobox help-message" id="motd">
Chat room implemented using
<a href="http://code.google.com/p/mongoose" target="_blank">Mongoose</a>
embeddable web server.
This application was written for educational purposes demonstrating
how web interface could be decoupled from the business logic. Not a
single line of HTML is generated by the server, instead, server
gives data to the client in JSON format.
</div>
</div>
<div>
<div id="middle">
<div><center><span id="error" class="rounded"></span><center></div>
<div id="menu">
<div class="menu-item left-rounded menu-item-selected"
name="chat">Chat</div>
<div class="menu-item left-rounded" name="settings">Settings</div>
</div>
<div id="content" class="rounded">
<div id="chat" class="main">
<div class="chat-window">
<span class="top-rounded chat-title">Main room</span>
<div class="bottom-rounded chat-content">
<div class="message-list" id="mml">
</div>
<input type="text" size="40" class="message-input"></input>
<span class="help-message">
Type your message here and press enter</span>
</div>
</div>
</div>
<div id="settings" class="hidden main">
<div>
<span class="top-rounded chat-title">Settings</span>
<div class="bottom-rounded chat-content">
<table>
<tr><td>Max messages to display:</td><td>blah blah</td></tr>
<tr><td>Text color:</td><td>blah blah</td></tr>
</table>
</div>
</div>
</div>
</div>
</div>
<div id="footer">
Copyright &copy; 2004-2010 by Sergey Lyubka
</div>
</body>
</html>
-154
View File
@@ -1,154 +0,0 @@
/*!
* jQuery JavaScript Library v1.4.2
* http://jquery.com/
*
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* Includes Sizzle.js
* http://sizzlejs.com/
* Copyright 2010, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
*
* Date: Sat Feb 13 22:33:48 2010 -0500
*/
(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
-43
View File
@@ -1,43 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" dir="ltr">
<!-- This file is part of the Mongoose project,
http://code.google.com/p/mongoose -->
<head>
<title>Mongoose chat: login</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<!--
Note that this page is self-sufficient, it does not load any other
CSS or Javascript file. This is done so because only this page is
allowed for non-authorized users. If we want to load other files
from the frontend, we need to change backend code to allow those
for non-authorized users. See chat.c :: must_authorize() function.
-->
</head>
<script>
window.onload = function() {
// Set correct action for the login form. We assume that the SSL port
// is the next one to insecure one.
var httpsPort = location.protocol.match(/https/) ? location.port :
parseInt(location.port) + 1;
document.forms[0].action = 'https://' + location.hostname + ':' +
httpsPort + '/authorize';
};
</script>
<body>
<center>
<h2>Mongoose chat server login</h2>
<div style="max-width: 30em;">
Username and password can be any non-empty strings.
</div>
<br/>
<form>
<input type="text" name="user"></input><br/>
<input type="text" name="password"></input><br/>
<input type="submit" value="Login"></input>
</form>
</center>
</body>
</html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

-106
View File
@@ -1,106 +0,0 @@
// This file is part of Mongoose project, http://code.google.com/p/mongoose
var chat = {
// Backend URL, string.
// 'http://backend.address.com' or '' if backend is the same as frontend
backendUrl: '',
maxVisibleMessages: 10,
errorMessageFadeOutTimeoutMs: 2000,
errorMessageFadeOutTimer: null,
lastMessageId: 0,
getMessagesIntervalMs: 1000,
};
chat.normalizeText = function(text) {
return text.replace('<', '&lt;').replace('>', '&gt;');
};
chat.refresh = function(data) {
if (data === undefined) {
return;
}
$.each(data, function(index, entry) {
var row = $('<div>').addClass('message-row').appendTo('#mml');
var timestamp = (new Date(entry.timestamp * 1000)).toLocaleTimeString();
$('<span>')
.addClass('message-timestamp')
.html('[' + timestamp + ']')
.prependTo(row);
$('<span>')
.addClass('message-user')
.addClass(entry.user ? '' : 'message-user-server')
.html(chat.normalizeText((entry.user || '[server]') + ':'))
.appendTo(row);
$('<span>')
.addClass('message-text')
.addClass(entry.user ? '' : 'message-text-server')
.html(chat.normalizeText(entry.text))
.appendTo(row);
chat.lastMessageId = Math.max(chat.lastMessageId, entry.id) + 1;
});
// Keep only chat.maxVisibleMessages, delete older ones.
while ($('#mml').children().length > chat.maxVisibleMessages) {
$('#mml div:first-child').remove();
}
};
chat.getMessages = function(enter_loop) {
$.ajax({
dataType: 'jsonp',
url: chat.backendUrl + '/ajax/get_messages',
data: {last_id: chat.lastMessageId},
success: chat.refresh,
error: function() {
},
});
if (enter_loop) {
window.setTimeout('chat.getMessages(true)', chat.getMessagesIntervalMs);
}
};
chat.handleMenuItemClick = function(ev) {
$('.menu-item').removeClass('menu-item-selected'); // Deselect menu buttons
$(this).addClass('menu-item-selected'); // Select clicked button
$('.main').addClass('hidden'); // Hide all main windows
$('#' + $(this).attr('name')).removeClass('hidden'); // Show main window
};
chat.showError = function(message) {
$('#error').html(message).fadeIn('fast');
window.clearTimeout(chat.errorMessageFadeOutTimer);
chat.errorMessageFadeOutTimer = window.setTimeout(function() {
$('#error').fadeOut('slow');
}, chat.errorMessageFadeOutTimeoutMs);
};
chat.handleMessageInput = function(ev) {
var input = ev.target;
if (ev.keyCode != 13 || !input.value)
return;
//input.disabled = true;
$.ajax({
dataType: 'jsonp',
url: chat.backendUrl + '/ajax/send_message',
data: {text: input.value},
success: function(ev) {
input.value = '';
input.disabled = false;
chat.getMessages(false);
},
error: function(ev) {
chat.showError('Error sending message');
input.disabled = false;
},
});
};
$(document).ready(function() {
$('.menu-item').click(chat.handleMenuItemClick);
$('.message-input').keypress(chat.handleMessageInput);
chat.getMessages(true);
});
// vim:ts=2:sw=2:et
-154
View File
@@ -1,154 +0,0 @@
/*
* vim:ts=2:sw=2:et:ai
*/
body {
font: 13px Arial; margin: 0.5em 1em;
}
#logo {
background: url('logo.png') no-repeat ;
width: 160px;
height: 40px;
float: left;
}
td {
text-align: left;
}
#motd {
margin-left: 170px;
}
.infobox {
background: #eed;
padding: 1px 1em;
}
.help-message {
color: #aaa;
}
#middle {
margin: 0.5em 0;
}
#error {
background: #c44;
color: white;
font-weight: bold;
}
#content, .menu-item-selected, .chat-title, .chat-content {
background: #c3d9ff;
}
#content {
overflow: hidden;
min-height: 7em;
padding: 1em;
}
.chat-title {
padding: 1px 1ex;
}
.chat-content {
padding: 1ex;
}
.chat-window {
}
.message-row {
margin: 2px;
border-bottom: 1px solid #bbb;
}
.message-timestamp {
color: #484;
}
.message-user {
margin-left: 0.5em;
font-weight: bold;
}
.message-text {
margin-left: 0.5em;
}
.message-user-server {
color: purple;
}
.message-text-server {
font-style: italic;
}
.main {
padding: 0.5em;
background: #f0fcff;
}
#menu {
margin-top: 1em;
min-width: 7em;
float: left;
}
#footer {
position: fixed;
bottom: 0;
right: 0;
color: #ccc;
padding: 0.5em;
}
.section {
clear: both;
}
.hidden {
display: none;
}
.menu-item {
cursor: pointer;
padding: 0.1em 0.5em;
}
.menu-item-selected {
font-weight: bold;
}
.message-list {
min-height: 1em;
background: white;
margin: 0.5em 0;
}
.rounded {
border-radius: 6px;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
}
.left-rounded {
border-radius: 6px 0 0 6px;
-moz-border-radius: 6px 0 0 6px;
-webkit-border-radius: 6px 0 0 6px;
}
.bottom-rounded {
border-radius: 0 0 6px 6px;
-moz-border-radius: 0 0 6px 6px;
-webkit-border-radius: 0 0 6px 6px;
}
.top-rounded {
border-radius: 6px 6px 0 0;
-moz-border-radius: 6px 6px 0 0;
-webkit-border-radius: 6px 6px 0 0;
}
+12
View File
@@ -0,0 +1,12 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = http_client
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
SOURCES = $(PROG).c ../../mongoose.c
unix: $(SOURCES)
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp *.o *.lib
+82
View File
@@ -0,0 +1,82 @@
// Copyright (c) 2014 Cesanta Software
// All rights reserved
//
// This example demostrates how to connect to the remote Web server,
// download data, process it and send back a reply.
#include <signal.h>
#include <stdlib.h>
#include "mongoose.h"
static int s_received_signal = 0;
static struct mg_server *s_server = NULL;
static const char *s_remote_addr = "glosbe.com:80";
static void signal_handler(int sig_num) {
signal(sig_num, signal_handler);
s_received_signal = sig_num;
}
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
struct mg_connection *client, *orig;
switch (ev) {
case MG_AUTH:
return MG_TRUE;
case MG_CONNECT:
// Send request to the remote host.
// TODO(lsm): handle connect error here.
mg_printf(conn, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",
"/gapi/translate?from=eng&dest=fra&format=json&phrase=cat",
s_remote_addr);
return MG_TRUE;
case MG_REPLY:
// Send reply to the original connection
orig = (struct mg_connection *) conn->connection_param;
mg_send_status(orig, conn->status_code);
mg_send_header(orig, "Content-Type", "text/plain");
mg_send_data(orig, conn->content, conn->content_len);
mg_send_data(orig, "", 0); // Last chunk: mark the end of reply
// Disconnect connections
orig->connection_param = NULL;
conn->connection_param = NULL;
return MG_TRUE;
case MG_REQUEST:
if ((client = mg_connect(s_server, s_remote_addr)) != NULL) {
// Interconnect requests
client->connection_param = conn;
conn->connection_param = client;
return MG_MORE;
} else {
mg_printf_data(conn, "%s", "cannot send API request");
return MG_TRUE;
}
default:
return MG_FALSE;
}
}
int main(void) {
s_server = mg_create_server(NULL, ev_handler);
mg_set_option(s_server, "listening_port", "8080");
// Setup signal handlers
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
printf("Listening on port %s\n", mg_get_option(s_server, "listening_port"));
while (s_received_signal == 0) {
mg_poll_server(s_server, 1000);
}
mg_destroy_server(&s_server);
printf("Existing on signal %d\n", s_received_signal);
return EXIT_SUCCESS;
}
-15
View File
@@ -1,15 +0,0 @@
HTTP/1.0 200 OK
Content-Type: text/plain
<?
-- Directory scanning example
function scandir(dir)
local t = {}
for f in io.popen('dir ' .. dir):lines() do table.insert(t, f) end
return t
end
local tab = scandir('C:\\')
for k,v in pairs(tab) do mg.write(v, '\n') end
?>
-42
View File
@@ -1,42 +0,0 @@
HTTP/1.0 200 OK
Content-Type: text/html
<html>
<p>Prime numbers from 0 to 100, calculated by Lua:</p>
<?
function is_prime(n)
if n <= 0 then return false end
if n <= 2 then return true end
if (n % 2 == 0) then return false end
for i = 3, n / 2, 2 do
if (n % i == 0) then return false end
end
return true
end
for i = 1, 100 do
if is_prime(i) then mg.write('<span>' .. i .. '</span>&nbsp;') end
end
?>
<p>Reading POST data from Lua (click submit):</p>
<form method="POST" ><input type="text" name="t1"/><input type="submit"></form>
<pre>
POST data: [<? mg.write(mg.request_info.content) ?>]
request method: [<? mg.write(mg.request_info.request_method) ?>]
IP/port: [<?mg.write(mg.request_info.remote_ip, ':',
mg.request_info.remote_port) ?>]
URI: [<? mg.write(mg.request_info.uri) ?>]
HTTP version [<? mg.write(mg.request_info.http_version) ?>]
Content Len: [<? mg.write(mg.request_info.content_len) ?>]
HEADERS:
<?
for name, value in pairs(mg.request_info.http_headers) do
mg.write(name, ':', value, '\n')
end
?>
</pre>
</html>
-17
View File
@@ -1,17 +0,0 @@
#include <stdio.h>
#include "lua_5.2.1.h"
static int smile(lua_State *L) {
(void) L; // Unused
printf("%s\n", ":-)");
return 0;
}
int LUA_API luaopen_lua_dll(lua_State *L) {
static const struct luaL_Reg api[] = {
{"smile", smile},
{NULL, NULL},
};
luaL_openlib(L, "lua_dll", api, 0);
return 1;
}
+12
View File
@@ -0,0 +1,12 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = mjpg_streamer
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
SOURCES = $(PROG).c ../../mongoose.c
$(PROG): $(SOURCES)
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
+105
View File
@@ -0,0 +1,105 @@
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "mongoose.h"
static void send_file(struct mg_connection *conn, const char *path) {
char buf[1024];
struct stat st;
int n;
FILE *fp;
if (stat(path, &st) == 0 && (fp = fopen(path, "rb")) != NULL) {
mg_printf(conn, "--w00t\r\nContent-Type: image/jpeg\r\n"
"Content-Length: %lu\r\n\r\n", (unsigned long) st.st_size);
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
mg_write(conn, buf, n);
}
fclose(fp);
mg_write(conn, "\r\n", 2);
}
}
struct conn_state {
int file_index;
time_t last_poll;
};
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
const char **file_names = (const char **) conn->server_param;
struct conn_state *state;
time_t now = time(NULL);
switch (ev) {
case MG_AUTH:
return MG_TRUE;
case MG_REQUEST:
if (strcmp(conn->uri, "/stream") != 0) {
mg_send_header(conn, "Content-Type", "text/html");
mg_printf_data(conn, "%s",
"Go to <a href=/stream>/stream</a> for MJPG stream");
return MG_TRUE;
}
mg_printf(conn, "%s",
"HTTP/1.0 200 OK\r\n" "Cache-Control: no-cache\r\n"
"Pragma: no-cache\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\n"
"Connection: close\r\nContent-Type: multipart/x-mixed-replace; "
"boundary=--w00t\r\n\r\n");
send_file(conn, file_names[0]);
state = (struct conn_state *) malloc(sizeof(*state));
conn->connection_param = state;
state->file_index = 1; // First file is already sent
state->last_poll = time(NULL);
return MG_MORE;
case MG_POLL:
state = (struct conn_state *) conn->connection_param;
if (state != NULL && now > state->last_poll) {
if (file_names[state->file_index] != NULL) {
send_file(conn, file_names[state->file_index]);
state->file_index++;
if (file_names[state->file_index] == NULL) {
return MG_TRUE; // No more images, close connection
}
}
state->last_poll = now;
}
return MG_FALSE;
case MG_CLOSE:
free(conn->connection_param);
conn->connection_param = NULL;
return MG_FALSE;
default:
return MG_FALSE;
}
}
int main(int argc, char *argv[]) {
struct mg_server *server;
if (argc < 3) {
printf("Usage: %s image1.jpg image2.jpg ...\n", argv[0]);
return 1;
}
server = mg_create_server(&argv[1], ev_handler);
mg_set_option(server, "listening_port", "8080");
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 1000);
}
mg_destroy_server(&server);
return 0;
}
+12
View File
@@ -0,0 +1,12 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = multi_threaded_server
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
SOURCES = $(PROG).c ../../mongoose.c
$(PROG): $(SOURCES)
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
@@ -2,11 +2,17 @@
// Start a browser and hit refresh couple of times. The replies will
// come from both server instances.
static int request_handler(struct mg_connection *conn) {
mg_send_header(conn, "Content-Type", "text/plain");
mg_printf_data(conn, "This is a reply from server instance # %s",
(char *) conn->server_param);
return MG_REQUEST_PROCESSED;
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
if (ev == MG_REQUEST) {
mg_send_header(conn, "Content-Type", "text/plain");
mg_printf_data(conn, "This is a reply from server instance # %s",
(char *) conn->server_param);
return MG_TRUE;
} else if (ev == MG_AUTH) {
return MG_TRUE;
} else {
return MG_FALSE;
}
}
static void *serve(void *server) {
@@ -17,15 +23,12 @@ static void *serve(void *server) {
int main(void) {
struct mg_server *server1, *server2;
server1 = mg_create_server((void *) "1");
server2 = mg_create_server((void *) "2");
server1 = mg_create_server((void *) "1", ev_handler);
server2 = mg_create_server((void *) "2", ev_handler);
mg_set_request_handler(server1, request_handler);
mg_set_request_handler(server2, request_handler);
// Make both server1 and server2 listen on the same socket
// Make both server1 and server2 listen on the same sockets
mg_set_option(server1, "listening_port", "8080");
mg_set_listening_socket(server2, mg_get_listening_socket(server1));
mg_copy_listeners(server1, server2);
// server1 goes to separate thread, server 2 runs in main thread.
// IMPORTANT: NEVER LET DIFFERENT THREADS HANDLE THE SAME SERVER.
+13
View File
@@ -0,0 +1,13 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = proxy_server
FLAGS = -I../.. -DNS_ENABLE_SSL
CFLAGS = -W -Wall -g -O0 -pthread -lssl $(FLAGS) $(CFLAGS_EXTRA)
SOURCES = $(PROG).c ../../mongoose.c
unix: $(SOURCES)
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
+202
View File
@@ -0,0 +1,202 @@
// Copyright (c) 2014 Cesanta Software Limited
// All rights reserved
//
// To build and run this example:
// git clone https://github.com/cesanta/net_skeleton.git
// git clone https://github.com/cesanta/mongoose.git
// cd mongoose/examples
// make proxy
// ./proxy
//
// Configure your browser to use localhost:2014 as a proxy for all protocols
// Then, navigate to https://cesanta.com
#include <sys/stat.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
#define sleep(x) Sleep((x) * 1000)
#else
#include <unistd.h>
#endif
#include "mongoose.h"
static int s_received_signal = 0;
static struct mg_server *s_server = NULL;
#define SSE_CONNECTION ((void *) 1)
static void elog(int do_exit, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputc('\n', stderr);
if (do_exit) exit(EXIT_FAILURE);
}
static void signal_handler(int sig_num) {
signal(sig_num, signal_handler);
s_received_signal = sig_num;
}
static int sse_push(struct mg_connection *conn, enum mg_event ev) {
if (ev == MG_POLL && conn->connection_param == SSE_CONNECTION) {
mg_printf(conn, "data: %s\r\n\r\n", (const char *) conn->callback_param);
}
return MG_TRUE;
}
static void *sse_pusher_thread_func(void *param) {
while (s_received_signal == 0) {
mg_wakeup_server_ex(s_server, sse_push, "%lu %s",
(unsigned long) time(NULL), (const char *) param);
sleep(1);
}
return NULL;
}
// Return: 1 if regular file, 2 if directory, 0 if not found
static int exists(const char *path) {
struct stat st;
return stat(path, &st) != 0 ? 0 : S_ISDIR(st.st_mode) == 0 ? 1 : 2;
}
// Return: 1 if regular file, 2 if directory, 0 if not found
static int is_local_file(const char *uri, char *path, size_t path_len) {
snprintf(path, path_len, "%s/%s",
mg_get_option(s_server, "document_root"), uri);
return exists(path);
}
static int try_to_serve_locally(struct mg_connection *conn) {
char path[500], buf[2000];
int n, res;
FILE *fp = NULL;
if ((res = is_local_file(conn->uri, path, sizeof(path))) == 2) {
strncat(path, "/index.html", sizeof(path) - strlen(path) - 1);
res = exists(path);
printf("PATH: [%s]\n", path);
}
if (res == 0) return MG_FALSE;
if ((fp = fopen(path, "rb")) != NULL) {
printf("Serving [%s] locally \n", path);
mg_send_header(conn, "Connection", "close");
mg_send_header(conn, "Content-Type", mg_get_mime_type(path, "text/plain"));
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
mg_send_data(conn, buf, n);
}
mg_send_data(conn, "", 0);
fclose(fp);
}
return fp == NULL ? MG_FALSE : MG_TRUE;
}
static int is_resource_present_locally(const char *uri) {
char path[500];
return is_local_file(uri, path, sizeof(path)) || strcmp(uri, "/api/sse") == 0;
}
static int proxy_event_handler(struct mg_connection *conn, enum mg_event ev) {
static const char target_url[] = "http://cesanta.com";
static int target_url_size = sizeof(target_url) - 1;
const char *host;
switch (ev) {
case MG_REQUEST:
host = mg_get_header(conn, "Host");
printf("[%s] [%s] [%s]\n", conn->request_method, conn->uri,
host == NULL ? "" : host);
if (strstr(conn->uri, "/qqq") != NULL) s_received_signal = SIGTERM;
// Proxied HTTPS requests use "CONNECT foo.com:443"
// Proxied HTTP requests use "GET http://..... "
// Serve requests for target_url from the local FS.
if (memcmp(conn->uri, target_url, target_url_size) == 0 &&
is_resource_present_locally(conn->uri + target_url_size)) {
conn->uri += target_url_size; // Leave only path in the URI
}
if (strcmp(conn->uri, "/api/sse") == 0) {
conn->connection_param = SSE_CONNECTION;
mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n"
"Content-Type: text/event-stream\r\n"
"Cache-Control: no-cache\r\n\r\n");
return MG_MORE;
}
if (host != NULL && strstr(host, "cesanta") != NULL) {
return try_to_serve_locally(conn);
}
// Enable man-in-the-middle SSL mode for oracle.com
if (!strcmp(conn->request_method, "CONNECT") &&
!strcmp(host, "oracle.com")) {
mg_terminate_ssl(conn, "ssl_cert.pem"); // MUST return MG_MORE after
return MG_MORE;
}
return MG_FALSE;
case MG_AUTH:
return MG_TRUE;
default:
return MG_FALSE;
}
}
static void setopt(struct mg_server *s, const char *opt, const char *val) {
const char *err_msg = mg_set_option(s, opt, val);
if (err_msg != NULL) {
elog(1, "Error setting [%s]: [%s]", opt, err_msg);
}
}
int main(int argc, char *argv[]) {
const char *port = "2014", *dump = NULL, *root = "proxy_web_root";
int i;
// Parse command line options
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) {
port = argv[++i];
} else if (strcmp(argv[i], "-root") == 0 && i + 1 < argc) {
root = argv[++i];
} else if (strcmp(argv[i], "-dump") == 0 && i + 1 < argc) {
dump = argv[++i];
} else {
elog(1, "Usage: %s [-cert FILE] [-ca_cert FILE] [-port PORT]", argv[0]);
}
}
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
// Create and configure proxy server
s_server = mg_create_server(NULL, &proxy_event_handler);
setopt(s_server, "enable_proxy", "yes");
setopt(s_server, "document_root", root);
setopt(s_server, "listening_port", port);
setopt(s_server, "hexdump_file", dump);
// Start two SSE pushing threads
mg_start_thread(sse_pusher_thread_func, (void *) "sse_pusher_thread_1");
mg_start_thread(sse_pusher_thread_func, (void *) "sse_pusher_thread_2");
// Start serving in the main thread
printf("Starting on port %s\n", mg_get_option(s_server, "listening_port"));
while (s_received_signal == 0) {
mg_poll_server(s_server, 1000);
}
printf("Existing on signal %d\n", s_received_signal);
mg_destroy_server(&s_server);
return EXIT_SUCCESS;
}
@@ -0,0 +1,23 @@
<html>
<head>
<title>App1 Index</title>
<style>
img { height: 40px; }
</style>
</head>
<body>
<h1>App1 index page. Served locally from the the proxy server filesystem</h1>
<p>image that references non-existent local resource. Forwarded to
the 'real' proxy target:</p>
<img src="http://cesanta.com/images/logo.png" />
<p>Google logo via HTTPS (external resource, served by remote host):</p>
<img src="https://www.google.ie/images/srpr/logo11w.png" />
<p>Same image via HTTP:</p>
<img src="http://www.google.ie/images/srpr/logo11w.png" />
</body>
</html>
@@ -0,0 +1,37 @@
<html>
<head>
<title>App2 Index</title>
<meta charset="utf-8">
<script>
window.onload = function() {
// Using secure websocket connection, wss://
var ws = new WebSocket('wss://echo.websocket.org');
var div = document.getElementById('events');
ws.onmessage = function(ev) {
var el = document.createElement('div');
el.innerHTML = 'websocket message: ' + ev.data;
div.appendChild(el);
// Keep only last 5 messages in the list
while (div.childNodes.length > 5) div.removeChild(div.firstChild);
};
// Send random stuff to the websocket connection periodically.
// websocket server much echo that stuff back.
window.setInterval(function() {
var d = new Date();
ws.send(d.toString());
}, 1000);
};
</script>
</head>
<body>
<h1>App2 index page. Served locally from the
the proxy's filesystem.</h1>
<p>
Following div shows proxy forwarding of websocket connection, served by
ws://echo.websocket.org:
</p>
<div id="events"></div>
</body>
</html>
@@ -0,0 +1,29 @@
<html>
<head>
<title> proxy index </title>
<script type="text/javascript">
window.onload = function() {
var es = new EventSource("/api/sse");
var div = document.getElementById('events');
es.onmessage = function(ev) {
var el = document.createElement('div');
el.innerHTML = 'sse message: ' + ev.data;
div.appendChild(el);
// Keep only last 5 messages in the list
while (div.childNodes.length > 5) div.removeChild(div.firstChild);
};
};
</script>
</head>
<body>
<h1> proxy index page.</h1>
<ul>
<li><a href="app1">App1</a> - App1 root</li>
<li><a href="app2">App2</a> - App2 root</li>
</ul>
<h2>SSE pushes, done by separate threads at random times:</h2>
<div id="events"></div>
</body>
</html>
File renamed without changes.
+12
View File
@@ -0,0 +1,12 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = restful_api
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
SOURCES = $(PROG).c ../../mongoose.c
$(PROG): $(SOURCES)
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
+66
View File
@@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>RESTful API demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
* { outline: none; font: 16px/1.4 Helvetica, Arial, sans-serif; }
body {
background-color: #cde; margin: 0;
padding: 0; font: 16px/1.4 Helvetica, Arial, sans-serif;
}
div.content {
width: 800px; margin: 2em auto; padding: 20px 50px;
background-color: #fff; border-radius: 1em;
}
label { display: inline-block; min-width: 7em; }
input { border: 1px solid #ccc; padding: 0.2em; }
a:link, a:visited { color: #69c; text-decoration: none; }
@media (max-width: 700px) {
body { background-color: #fff; }
div.content { width: auto; margin: 0 auto; padding: 1em; }
}
</style>
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script language="javascript" type="text/javascript">
jQuery(function() {
$(document).on('keyup', '#n1, #n2', function() {
$.ajax({
url: '/api/sum',
method: 'POST',
dataType: 'json',
data: { n1: $('#n1').val(), n2: $('#n2').val() },
success: function(json) {
$('#result').html(json.result);
}
});
});
});
</script>
</head>
<body>
<div class="content">
<h1>RESTful API demo.</h1>
<p>
This page demonstrates how Mongoose web server could be used to implement
RESTful APIs. Enter numbers below, and press Submit. Browser will send
two numbers to <tt>/api/sum</tt> URI, Mongoose calclulates the sum of
two and returns the result.
</p>
<div>
<label>Number 1:</label> <input type="text" id="n1" />
</div><div>
<label>Number 2:</label> <input type="text" id="n2" />
</div><div>
<label>Result:</label> <span id="result">&nbsp;</span>
</div><div>
</div>
</body>
</html>
+51
View File
@@ -0,0 +1,51 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mongoose.h"
static const char *s_no_cache_header =
"Cache-Control: max-age=0, post-check=0, "
"pre-check=0, no-store, no-cache, must-revalidate\r\n";
static void handle_restful_call(struct mg_connection *conn) {
char n1[100], n2[100];
// Get form variables
mg_get_var(conn, "n1", n1, sizeof(n1));
mg_get_var(conn, "n2", n2, sizeof(n2));
mg_printf_data(conn, "{ \"result\": %lf }", strtod(n1, NULL) + strtod(n2, NULL));
}
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
switch (ev) {
case MG_AUTH: return MG_TRUE;
case MG_REQUEST:
if (!strcmp(conn->uri, "/api/sum")) {
handle_restful_call(conn);
return MG_TRUE;
}
mg_send_file(conn, "index.html", s_no_cache_header);
return MG_MORE;
default: return MG_FALSE;
}
}
int main(void) {
struct mg_server *server;
// Create and configure the server
server = mg_create_server(NULL, ev_handler);
mg_set_option(server, "listening_port", "8000");
// Serve request. Hit Ctrl-C to terminate the program
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 1000);
}
// Cleanup, and free server instance
mg_destroy_server(&server);
return 0;
}
+21
View File
@@ -0,0 +1,21 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = send_file
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
SOURCES = $(PROG).c ../../mongoose.c
all: $(PROG)
run: $(PROG)
./$(PROG)
$(PROG): $(SOURCES) Makefile
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
win:
wine cl $(SOURCES) /MD /nologo /DNDEBUG /O1 /I../.. /Fe$(PROG).exe
wine $(PROG).exe
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib *.gc*
+27
View File
@@ -0,0 +1,27 @@
// Copyright (c) 2014 Cesanta Software
// All rights reserved
//
// This example demostrates how to send arbitrary files to the client.
#include "mongoose.h"
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
switch (ev) {
case MG_REQUEST:
mg_send_file(conn, "send_file.c", NULL); // Also could be a dir, or CGI
return MG_MORE; // It is important to return MG_MORE after mg_send_file!
case MG_AUTH: return MG_TRUE;
default: return MG_FALSE;
}
}
int main(void) {
struct mg_server *server = mg_create_server(NULL, ev_handler);
mg_set_option(server, "listening_port", "8080");
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) mg_poll_server(server, 1000);
mg_destroy_server(&server);
return 0;
}
+12
View File
@@ -0,0 +1,12 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = web_server
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
SOURCES = $(PROG).c ../../mongoose.c
$(PROG): $(SOURCES)
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
@@ -1,480 +1,491 @@
// Copyright (c) 2004-2013 Sergey Lyubka
// Copyright (c) 2013-2014 Cesanta Software Limited
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#undef UNICODE // Use ANSI WinAPI functions
#undef _UNICODE // Use multibyte encoding on Windows
#define _MBCS // Use multibyte encoding on Windows
#define _WIN32_WINNT 0x500 // Enable MIIM_BITMAP
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
#define _XOPEN_SOURCE 600 // For PATH_MAX on linux
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include <stdarg.h>
#include <ctype.h>
#include "mongoose.h"
#ifdef _WIN32
#include <windows.h>
#include <direct.h> // For chdir()
#include <winsvc.h>
#include <shlobj.h>
#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif
#ifndef S_ISDIR
#define S_ISDIR(x) ((x) & _S_IFDIR)
#endif
#define DIRSEP '\\'
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define sleep(x) Sleep((x) * 1000)
#define abs_path(rel, abs, abs_size) _fullpath((abs), (rel), (abs_size))
#define SIGCHLD 0
typedef struct _stat file_stat_t;
#define stat(x, y) _stat((x), (y))
#else
typedef struct stat file_stat_t;
#include <sys/wait.h>
#include <unistd.h>
#define DIRSEP '/'
#define __cdecl
#define abs_path(rel, abs, abs_size) realpath((rel), (abs))
#endif // _WIN32
#define MAX_OPTIONS 100
#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
static int exit_flag;
static char server_name[50]; // Set by init_server_name()
static char config_file[PATH_MAX]; // Set by process_command_line_arguments()
static struct mg_server *server; // Set by start_mongoose()
static const char *s_default_document_root = ".";
static const char *s_default_listening_port = "8080";
#if !defined(CONFIG_FILE)
#define CONFIG_FILE "mongoose.conf"
#endif /* !CONFIG_FILE */
static void __cdecl signal_handler(int sig_num) {
// Reinstantiate signal handler
signal(sig_num, signal_handler);
#ifndef _WIN32
// Do not do the trick with ignoring SIGCHLD, cause not all OSes (e.g. QNX)
// reap zombies if SIGCHLD is ignored. On QNX, for example, waitpid()
// fails if SIGCHLD is ignored, making system() non-functional.
if (sig_num == SIGCHLD) {
do {} while (waitpid(-1, &sig_num, WNOHANG) > 0);
} else
#endif
{ exit_flag = sig_num; }
}
static void vnotify(const char *fmt, va_list ap, int must_exit) {
char msg[200];
vsnprintf(msg, sizeof(msg), fmt, ap);
fprintf(stderr, "%s\n", msg);
if (must_exit) {
exit(EXIT_FAILURE);
}
}
static void notify(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vnotify(fmt, ap, 0);
va_end(ap);
}
static void die(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vnotify(fmt, ap, 1);
va_end(ap);
}
static void show_usage_and_exit(void) {
const char **names;
int i;
fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built on %s\n",
MONGOOSE_VERSION, __DATE__);
fprintf(stderr, "Usage:\n");
#ifndef MONGOOSE_NO_AUTH
fprintf(stderr, " mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
#endif
fprintf(stderr, " mongoose [config_file]\n");
fprintf(stderr, " mongoose [-option value ...]\n");
fprintf(stderr, "\nOPTIONS:\n");
names = mg_get_valid_option_names();
for (i = 0; names[i] != NULL; i += 2) {
fprintf(stderr, " -%s %s\n",
names[i], names[i + 1] == NULL ? "<empty>" : names[i + 1]);
}
exit(EXIT_FAILURE);
}
static char *sdup(const char *str) {
char *p;
if ((p = (char *) malloc(strlen(str) + 1)) != NULL) {
strcpy(p, str);
}
return p;
}
static void set_option(char **options, const char *name, const char *value) {
int i;
for (i = 0; i < MAX_OPTIONS - 3; i++) {
if (options[i] == NULL) {
options[i] = sdup(name);
options[i + 1] = sdup(value);
options[i + 2] = NULL;
break;
} else if (!strcmp(options[i], name)) {
free(options[i + 1]);
options[i + 1] = sdup(value);
break;
}
}
if (i == MAX_OPTIONS - 3) {
die("%s", "Too many options specified");
}
}
static void process_command_line_arguments(char *argv[], char **options) {
char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)], *p;
FILE *fp = NULL;
size_t i, cmd_line_opts_start = 1, line_no = 0;
// Should we use a config file ?
if (argv[1] != NULL && argv[1][0] != '-') {
snprintf(config_file, sizeof(config_file), "%s", argv[1]);
cmd_line_opts_start = 2;
} else if ((p = strrchr(argv[0], DIRSEP)) == NULL) {
// No command line flags specified. Look where binary lives
snprintf(config_file, sizeof(config_file), "%s", CONFIG_FILE);
} else {
snprintf(config_file, sizeof(config_file), "%.*s%c%s",
(int) (p - argv[0]), argv[0], DIRSEP, CONFIG_FILE);
}
fp = fopen(config_file, "r");
// If config file was set in command line and open failed, die
if (cmd_line_opts_start == 2 && fp == NULL) {
die("Cannot open config file %s: %s", config_file, strerror(errno));
}
// Load config file settings first
if (fp != NULL) {
fprintf(stderr, "Loading config file %s\n", config_file);
// Loop over the lines in config file
while (fgets(line, sizeof(line), fp) != NULL) {
line_no++;
// Ignore empty lines and comments
for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++;
if (line[i] == '#' || line[i] == '\0') {
continue;
}
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
printf("%s: line %d is invalid, ignoring it:\n %s",
config_file, (int) line_no, line);
} else {
set_option(options, opt, val);
}
}
fclose(fp);
}
// If we're under MacOS and started by launchd, then the second
// argument is process serial number, -psn_.....
// In this case, don't process arguments at all.
if (argv[1] == NULL || memcmp(argv[1], "-psn_", 5) != 0) {
// Handle command line flags.
// They override config file and default settings.
for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
if (argv[i][0] != '-' || argv[i + 1] == NULL) {
show_usage_and_exit();
}
set_option(options, &argv[i][1], argv[i + 1]);
}
}
}
static void init_server_name(void) {
const char *descr = "";
snprintf(server_name, sizeof(server_name), "Mongoose web server v.%s%s",
MONGOOSE_VERSION, descr);
}
static int is_path_absolute(const char *path) {
#ifdef _WIN32
return path != NULL &&
((path[0] == '\\' && path[1] == '\\') || // UNC path, e.g. \\server\dir
(isalpha(path[0]) && path[1] == ':' && path[2] == '\\')); // E.g. X:\dir
#else
return path != NULL && path[0] == '/';
#endif
}
static char *get_option(char **options, const char *option_name) {
int i;
for (i = 0; options[i] != NULL; i++)
if (!strcmp(options[i], option_name))
return options[i + 1];
return NULL;
}
static int path_exists(const char *path, int is_dir) {
file_stat_t st;
return path == NULL || (stat(path, &st) == 0 &&
((S_ISDIR(st.st_mode) ? 1 : 0) == is_dir));
}
static void verify_existence(char **options, const char *name, int is_dir) {
const char *path = get_option(options, name);
if (!path_exists(path, is_dir)) {
notify("Invalid path for %s: [%s]: (%s). Make sure that path is either "
"absolute, or it is relative to mongoose executable.",
name, path, strerror(errno));
}
}
static void set_absolute_path(char *options[], const char *option_name,
const char *path_to_mongoose_exe) {
char path[PATH_MAX], abs[PATH_MAX], *option_value;
const char *p;
// Check whether option is already set
option_value = get_option(options, option_name);
// If option is already set and it is an absolute path,
// leave it as it is -- it's already absolute.
if (option_value != NULL && !is_path_absolute(option_value)) {
// Not absolute. Use the directory where mongoose executable lives
// be the relative directory for everything.
// Extract mongoose executable directory into path.
if ((p = strrchr(path_to_mongoose_exe, DIRSEP)) == NULL) {
getcwd(path, sizeof(path));
} else {
snprintf(path, sizeof(path), "%.*s", (int) (p - path_to_mongoose_exe),
path_to_mongoose_exe);
}
strncat(path, "/", sizeof(path) - 1);
strncat(path, option_value, sizeof(path) - 1);
// Absolutize the path, and set the option
abs_path(path, abs, sizeof(abs));
set_option(options, option_name, abs);
}
}
#ifndef MONGOOSE_NO_AUTH
int modify_passwords_file(const char *fname, const char *domain,
const char *user, const char *pass) {
int found;
char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
FILE *fp, *fp2;
found = 0;
fp = fp2 = NULL;
// Regard empty password as no password - remove user record.
if (pass != NULL && pass[0] == '\0') {
pass = NULL;
}
(void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
// Create the file if does not exist
if ((fp = fopen(fname, "a+")) != NULL) {
fclose(fp);
}
// Open the given file and temporary file
if ((fp = fopen(fname, "r")) == NULL) {
return 0;
} else if ((fp2 = fopen(tmp, "w+")) == NULL) {
fclose(fp);
return 0;
}
// Copy the stuff to temporary file
while (fgets(line, sizeof(line), fp) != NULL) {
if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
continue;
}
if (!strcmp(u, user) && !strcmp(d, domain)) {
found++;
if (pass != NULL) {
mg_md5(ha1, user, ":", domain, ":", pass, NULL);
fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
}
} else {
fprintf(fp2, "%s", line);
}
}
// If new user, just add it
if (!found && pass != NULL) {
mg_md5(ha1, user, ":", domain, ":", pass, NULL);
fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
}
// Close files
fclose(fp);
fclose(fp2);
// Put the temp file in place of real file
remove(fname);
rename(tmp, fname);
return 1;
}
#endif
static void start_mongoose(int argc, char *argv[]) {
char *options[MAX_OPTIONS];
int i;
if ((server = mg_create_server(NULL)) == NULL) {
die("%s", "Failed to start Mongoose.");
}
#ifndef MONGOOSE_NO_AUTH
// Edit passwords file if -A option is specified
if (argc > 1 && !strcmp(argv[1], "-A")) {
if (argc != 6) {
show_usage_and_exit();
}
exit(modify_passwords_file(argv[2], argv[3], argv[4], argv[5]) ?
EXIT_SUCCESS : EXIT_FAILURE);
}
#endif
// Show usage if -h or --help options are specified
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
show_usage_and_exit();
}
options[0] = NULL;
set_option(options, "document_root", s_default_document_root);
set_option(options, "listening_port", s_default_listening_port);
// Update config based on command line arguments
process_command_line_arguments(argv, options);
// Make sure we have absolute paths for files and directories
// https://github.com/valenok/mongoose/issues/181
set_absolute_path(options, "document_root", argv[0]);
set_absolute_path(options, "dav_auth_file", argv[0]);
set_absolute_path(options, "cgi_interpreter", argv[0]);
set_absolute_path(options, "access_log_file", argv[0]);
set_absolute_path(options, "global_auth_file", argv[0]);
set_absolute_path(options, "ssl_certificate", argv[0]);
if (!path_exists(get_option(options, "document_root"), 1)) {
set_option(options, "document_root", s_default_document_root);
set_absolute_path(options, "document_root", argv[0]);
notify("Setting document_root to [%s]",
mg_get_option(server, "document_root"));
}
// Make extra verification for certain options
verify_existence(options, "document_root", 1);
verify_existence(options, "cgi_interpreter", 0);
verify_existence(options, "ssl_certificate", 0);
for (i = 0; options[i] != NULL; i += 2) {
const char *msg = mg_set_option(server, options[i], options[i + 1]);
if (msg != NULL) {
notify("Failed to set option [%s] to [%s]: %s",
options[i], options[i + 1], msg);
if (!strcmp(options[i], "listening_port")) {
mg_set_option(server, "listening_port", s_default_listening_port);
notify("Setting %s to [%s]", options[i], s_default_listening_port);
}
}
free(options[i]);
free(options[i + 1]);
}
// Change current working directory to document root. This way,
// scripts can use relative paths.
chdir(mg_get_option(server, "document_root"));
// Add an ability to pass listening socket to mongoose
{
const char *env = getenv("MONGOOSE_LISTENING_SOCKET");
if (env != NULL && atoi(env) > 0 ) {
mg_set_listening_socket(server, atoi(env));
}
}
// Setup signal handler: quit on Ctrl-C
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
#ifndef _WIN32
signal(SIGCHLD, signal_handler);
#endif
}
int main(int argc, char *argv[]) {
init_server_name();
start_mongoose(argc, argv);
printf("%s serving [%s] on port %s\n",
server_name, mg_get_option(server, "document_root"),
mg_get_option(server, "listening_port"));
fflush(stdout); // Needed, Windows terminals might not be line-buffered
while (exit_flag == 0) {
mg_poll_server(server, 1000);
}
printf("Exiting on signal %d ...", exit_flag);
fflush(stdout);
mg_destroy_server(&server);
printf("%s\n", " done.");
return EXIT_SUCCESS;
}
// Copyright (c) 2004-2013 Sergey Lyubka
// Copyright (c) 2013-2014 Cesanta Software Limited
#undef UNICODE // Use ANSI WinAPI functions
#undef _UNICODE // Use multibyte encoding on Windows
#define _MBCS // Use multibyte encoding on Windows
#define _WIN32_WINNT 0x500 // Enable MIIM_BITMAP
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
#define _XOPEN_SOURCE 600 // For PATH_MAX on linux
#undef WIN32_LEAN_AND_MEAN // Let windows.h always include winsock2.h
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include <stdarg.h>
#include <ctype.h>
#include <time.h>
#include "mongoose.h"
#ifdef _WIN32
#include <windows.h>
#include <direct.h> // For chdir()
#include <winsvc.h>
#include <shlobj.h>
#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif
#ifndef S_ISDIR
#define S_ISDIR(x) ((x) & _S_IFDIR)
#endif
#define DIRSEP '\\'
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define sleep(x) Sleep((x) * 1000)
#define abs_path(rel, abs, abs_size) _fullpath((abs), (rel), (abs_size))
#define SIGCHLD 0
typedef struct _stat file_stat_t;
#define stat(x, y) _stat((x), (y))
#else
typedef struct stat file_stat_t;
#include <sys/wait.h>
#include <unistd.h>
#ifdef IOS
#include <ifaddrs.h>
#endif
#define DIRSEP '/'
#define __cdecl
#define abs_path(rel, abs, abs_size) realpath((rel), (abs))
#endif // _WIN32
#define MAX_OPTIONS 100
#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
#ifndef MVER
#define MVER MONGOOSE_VERSION
#endif
static int exit_flag;
static char server_name[50]; // Set by init_server_name()
static char s_config_file[PATH_MAX]; // Set by process_command_line_arguments
static struct mg_server *server; // Set by start_mongoose()
static const char *s_default_document_root = ".";
static const char *s_default_listening_port = "8080";
static char **s_argv = { NULL };
static void set_options(char *argv[]);
#if !defined(CONFIG_FILE)
#define CONFIG_FILE "mongoose.conf"
#endif /* !CONFIG_FILE */
static void __cdecl signal_handler(int sig_num) {
// Reinstantiate signal handler
signal(sig_num, signal_handler);
#ifndef _WIN32
// Do not do the trick with ignoring SIGCHLD, cause not all OSes (e.g. QNX)
// reap zombies if SIGCHLD is ignored. On QNX, for example, waitpid()
// fails if SIGCHLD is ignored, making system() non-functional.
if (sig_num == SIGCHLD) {
do {} while (waitpid(-1, &sig_num, WNOHANG) > 0);
} else
#endif
{ exit_flag = sig_num; }
}
static void vnotify(const char *fmt, va_list ap, int must_exit) {
char msg[200];
vsnprintf(msg, sizeof(msg), fmt, ap);
fprintf(stderr, "%s\n", msg);
if (must_exit) {
exit(EXIT_FAILURE);
}
}
static void notify(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vnotify(fmt, ap, 0);
va_end(ap);
}
static void die(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vnotify(fmt, ap, 1);
va_end(ap);
}
static void show_usage_and_exit(void) {
const char **names;
int i;
fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built on %s\n",
MVER, __DATE__);
fprintf(stderr, "Usage:\n");
#if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM)
fprintf(stderr, " mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
#endif
fprintf(stderr, " mongoose [config_file]\n");
fprintf(stderr, " mongoose [-option value ...]\n");
fprintf(stderr, "\nOPTIONS:\n");
names = mg_get_valid_option_names();
for (i = 0; names[i] != NULL; i += 2) {
fprintf(stderr, " -%s %s\n",
names[i], names[i + 1] == NULL ? "<empty>" : names[i + 1]);
}
exit(EXIT_FAILURE);
}
#define EV_HANDLER NULL
static char *sdup(const char *str) {
char *p;
if ((p = (char *) malloc(strlen(str) + 1)) != NULL) {
strcpy(p, str);
}
return p;
}
static void set_option(char **options, const char *name, const char *value) {
int i;
for (i = 0; i < MAX_OPTIONS - 3; i++) {
if (options[i] == NULL) {
options[i] = sdup(name);
options[i + 1] = sdup(value);
options[i + 2] = NULL;
break;
} else if (!strcmp(options[i], name)) {
free(options[i + 1]);
options[i + 1] = sdup(value);
break;
}
}
if (i == MAX_OPTIONS - 3) {
die("%s", "Too many options specified");
}
}
static void process_command_line_arguments(char *argv[], char **options) {
char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)],
*p, cpath[PATH_MAX];
FILE *fp = NULL;
size_t i, cmd_line_opts_start = 1, line_no = 0;
// Should we use a config file ?
if (argv[1] != NULL && argv[1][0] != '-') {
snprintf(cpath, sizeof(cpath), "%s", argv[1]);
cmd_line_opts_start = 2;
} else if ((p = strrchr(argv[0], DIRSEP)) == NULL) {
// No command line flags specified. Look where binary lives
snprintf(cpath, sizeof(cpath), "%s", CONFIG_FILE);
} else {
snprintf(cpath, sizeof(cpath), "%.*s%c%s",
(int) (p - argv[0]), argv[0], DIRSEP, CONFIG_FILE);
}
abs_path(cpath, s_config_file, sizeof(s_config_file));
fp = fopen(s_config_file, "r");
// If config file was set in command line and open failed, die
if (cmd_line_opts_start == 2 && fp == NULL) {
die("Cannot open config file %s: %s", s_config_file, strerror(errno));
}
// Load config file settings first
if (fp != NULL) {
fprintf(stderr, "Loading config file %s\n", s_config_file);
// Loop over the lines in config file
while (fgets(line, sizeof(line), fp) != NULL) {
line_no++;
// Ignore empty lines and comments
for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++;
if (line[i] == '#' || line[i] == '\0') {
continue;
}
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
printf("%s: line %d is invalid, ignoring it:\n %s",
s_config_file, (int) line_no, line);
} else {
set_option(options, opt, val);
}
}
fclose(fp);
}
// If we're under MacOS and started by launchd, then the second
// argument is process serial number, -psn_.....
// In this case, don't process arguments at all.
if (argv[1] == NULL || memcmp(argv[1], "-psn_", 5) != 0) {
// Handle command line flags.
// They override config file and default settings.
for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
if (argv[i][0] != '-' || argv[i + 1] == NULL) {
show_usage_and_exit();
}
set_option(options, &argv[i][1], argv[i + 1]);
}
}
}
static void init_server_name(void) {
const char *descr = "";
snprintf(server_name, sizeof(server_name), "Mongoose web server v.%s%s",
MVER, descr);
}
static int is_path_absolute(const char *path) {
#ifdef _WIN32
return path != NULL &&
((path[0] == '\\' && path[1] == '\\') || // UNC path, e.g. \\server\dir
(isalpha(path[0]) && path[1] == ':' && path[2] == '\\')); // E.g. X:\dir
#else
return path != NULL && path[0] == '/';
#endif
}
static char *get_option(char **options, const char *option_name) {
int i;
for (i = 0; options[i] != NULL; i++)
if (!strcmp(options[i], option_name))
return options[i + 1];
return NULL;
}
static void *serving_thread_func(void *param) {
struct mg_server *srv = (struct mg_server *) param;
while (exit_flag == 0) {
mg_poll_server(srv, 1000);
}
return NULL;
}
static int path_exists(const char *path, int is_dir) {
file_stat_t st;
return path == NULL || (stat(path, &st) == 0 &&
((S_ISDIR(st.st_mode) ? 1 : 0) == is_dir));
}
static void verify_existence(char **options, const char *name, int is_dir) {
const char *path = get_option(options, name);
if (!path_exists(path, is_dir)) {
notify("Invalid path for %s: [%s]: (%s). Make sure that path is either "
"absolute, or it is relative to mongoose executable.",
name, path, strerror(errno));
}
}
static void set_absolute_path(char *options[], const char *option_name) {
char path[PATH_MAX], abs[PATH_MAX], *option_value;
const char *p;
// Check whether option is already set
option_value = get_option(options, option_name);
// If option is already set and it is an absolute path,
// leave it as it is -- it's already absolute.
if (option_value != NULL && !is_path_absolute(option_value)) {
// Not absolute. Use the directory where mongoose executable lives
// be the relative directory for everything.
// Extract mongoose executable directory into path.
if ((p = strrchr(s_config_file, DIRSEP)) == NULL) {
getcwd(path, sizeof(path));
} else {
snprintf(path, sizeof(path), "%.*s", (int) (p - s_config_file),
s_config_file);
}
strncat(path, "/", sizeof(path) - 1);
strncat(path, option_value, sizeof(path) - 1);
// Absolutize the path, and set the option
abs_path(path, abs, sizeof(abs));
set_option(options, option_name, abs);
}
}
#if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM)
int modify_passwords_file(const char *fname, const char *domain,
const char *user, const char *pass) {
int found;
char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
FILE *fp, *fp2;
found = 0;
fp = fp2 = NULL;
// Regard empty password as no password - remove user record.
if (pass != NULL && pass[0] == '\0') {
pass = NULL;
}
(void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
// Create the file if does not exist
if ((fp = fopen(fname, "a+")) != NULL) {
fclose(fp);
}
// Open the given file and temporary file
if ((fp = fopen(fname, "r")) == NULL) {
return 0;
} else if ((fp2 = fopen(tmp, "w+")) == NULL) {
fclose(fp);
return 0;
}
// Copy the stuff to temporary file
while (fgets(line, sizeof(line), fp) != NULL) {
if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
continue;
}
if (!strcmp(u, user) && !strcmp(d, domain)) {
found++;
if (pass != NULL) {
mg_md5(ha1, user, ":", domain, ":", pass, NULL);
fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
}
} else {
fprintf(fp2, "%s", line);
}
}
// If new user, just add it
if (!found && pass != NULL) {
mg_md5(ha1, user, ":", domain, ":", pass, NULL);
fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
}
// Close files
fclose(fp);
fclose(fp2);
// Put the temp file in place of real file
remove(fname);
rename(tmp, fname);
return 1;
}
#endif
static void start_mongoose(int argc, char *argv[]) {
s_argv = argv;
if ((server = mg_create_server(NULL, EV_HANDLER)) == NULL) {
die("%s", "Failed to start Mongoose.");
}
#if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM)
// Edit passwords file if -A option is specified
if (argc > 1 && !strcmp(argv[1], "-A")) {
if (argc != 6) {
show_usage_and_exit();
}
exit(modify_passwords_file(argv[2], argv[3], argv[4], argv[5]) ?
EXIT_SUCCESS : EXIT_FAILURE);
}
#endif
// Show usage if -h or --help options are specified
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
show_usage_and_exit();
}
set_options(argv);
}
static void set_options(char *argv[]) {
char *options[MAX_OPTIONS];
int i;
options[0] = NULL;
set_option(options, "document_root", s_default_document_root);
set_option(options, "listening_port", s_default_listening_port);
// Update config based on command line arguments
process_command_line_arguments(argv, options);
// Make sure we have absolute paths for files and directories
// https://github.com/valenok/mongoose/issues/181
set_absolute_path(options, "document_root");
set_absolute_path(options, "dav_auth_file");
set_absolute_path(options, "cgi_interpreter");
set_absolute_path(options, "access_log_file");
set_absolute_path(options, "global_auth_file");
set_absolute_path(options, "ssl_certificate");
if (!path_exists(get_option(options, "document_root"), 1)) {
set_option(options, "document_root", s_default_document_root);
set_absolute_path(options, "document_root");
notify("Setting document_root to [%s]",
mg_get_option(server, "document_root"));
}
// Make extra verification for certain options
verify_existence(options, "document_root", 1);
verify_existence(options, "cgi_interpreter", 0);
verify_existence(options, "ssl_certificate", 0);
for (i = 0; options[i] != NULL; i += 2) {
const char *msg = mg_set_option(server, options[i], options[i + 1]);
if (msg != NULL) {
notify("Failed to set option [%s] to [%s]: %s",
options[i], options[i + 1], msg);
if (!strcmp(options[i], "listening_port")) {
mg_set_option(server, "listening_port", s_default_listening_port);
notify("Setting %s to [%s]", options[i], s_default_listening_port);
}
}
free(options[i]);
free(options[i + 1]);
}
// Change current working directory to document root. This way,
// scripts can use relative paths.
chdir(mg_get_option(server, "document_root"));
#if 0
// Add an ability to pass listening socket to mongoose
{
const char *env = getenv("MONGOOSE_LISTENING_SOCKET");
if (env != NULL && atoi(env) > 0 ) {
mg_set_listening_socket(server, atoi(env));
}
}
#endif
// Setup signal handler: quit on Ctrl-C
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
#ifndef _WIN32
signal(SIGCHLD, signal_handler);
#endif
}
int main(int argc, char *argv[]) {
init_server_name();
start_mongoose(argc, argv);
printf("%s serving [%s] on port %s\n",
server_name, mg_get_option(server, "document_root"),
mg_get_option(server, "listening_port"));
fflush(stdout); // Needed, Windows terminals might not be line-buffered
serving_thread_func(server);
printf("Exiting on signal %d ...", exit_flag);
fflush(stdout);
mg_destroy_server(&server);
printf("%s\n", " done.");
return EXIT_SUCCESS;
}
-51
View File
@@ -1,51 +0,0 @@
#include <string.h>
#include "mongoose.h"
extern const char *find_embedded_file(const char *, size_t *);
static int iterate_callback(struct mg_connection *c) {
if (c->is_websocket) {
char buf[20];
int len = snprintf(buf, sizeof(buf), "%d", * (int *) c->connection_param);
mg_websocket_write(c, 1, buf, len);
}
return MG_REQUEST_PROCESSED;
}
static int index_html(struct mg_connection *conn) {
size_t index_size;
const char *index_html = find_embedded_file("websocket.html", &index_size);
if (conn->is_websocket) {
// This handler is called for each incoming websocket frame, one or more
// times for connection lifetime.
// Echo websocket data back to the client.
mg_websocket_write(conn, 1, conn->content, conn->content_len);
return conn->content_len == 4 && !memcmp(conn->content, "exit", 4) ?
MG_CLIENT_CLOSE : MG_CLIENT_CONTINUE;
} else {
mg_send_header(conn, "Content-Type", "text/html");
mg_send_data(conn, index_html, index_size);
return MG_REQUEST_PROCESSED;
}
}
int main(void) {
struct mg_server *server = mg_create_server(NULL);
unsigned int current_timer = 0, last_timer = 0;
mg_set_option(server, "listening_port", "8080");
mg_set_request_handler(server, index_html);
printf("Started on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
current_timer = mg_poll_server(server, 100);
if (current_timer - last_timer > 0) {
last_timer = current_timer;
mg_iterate_over_connections(server, iterate_callback, &current_timer);
}
}
mg_destroy_server(&server);
return 0;
}
+12
View File
@@ -0,0 +1,12 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = websocket_chat
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
SOURCES = $(PROG).c ../../mongoose.c
$(PROG): $(SOURCES)
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
+98
View File
@@ -0,0 +1,98 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>WebSocket Test</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
body {
background-color: #cde; margin: 0;
padding: 0; font: 14px Helvetica, Arial, sans-serif;
}
div.content {
width: 800px; margin: 2em auto; padding: 20px 50px;
background-color: #fff; border-radius: 1em;
}
#messages {
border: 2px solid #fec; border-radius: 1em;
height: 10em; overflow: scroll; padding: 0.5em 1em;
}
a:link, a:visited { color: #69c; text-decoration: none; }
@media (max-width: 700px) {
body { background-color: #fff; }
div.content {
width: auto; margin: 0 auto; border-radius: 0;
padding: 1em;
}
}
</style>
<script language="javascript" type="text/javascript">
var rooms = [];
var ws = new WebSocket('ws://' + location.host + '/ws');
if (!window.console) { window.console = { log: function() {} } };
ws.onopen = function(ev) { console.log(ev); };
ws.onerror = function(ev) { console.log(ev); };
ws.onclose = function(ev) { console.log(ev); };
ws.onmessage = function(ev) {
console.log(ev);
var m = (ev.data || '').match(/^(\S+) (.+)/);
if (m[1] == 'id') {
document.getElementById('my_id').innerText = m[2];
} else if (m[1] == 'msg') {
var div = document.createElement('div');
div.innerHTML = m[2];
document.getElementById('messages').appendChild(div);
}
};
window.onload = function() {
document.getElementById('send_button').onclick = function(ev) {
var msg = document.getElementById('send_input').value;
ws.send('msg ' + msg);
};
document.getElementById('room_sel').onchange = function(ev) {
var roomName = this.value || '?';
ws.send('join ' + roomName);
};
};
</script>
</head>
<body>
<div class="content">
<h1>Websocket PubSub Demonstration</h1>
<p>
This page demonstrates how Mongoose web server could be used to implement
<a href="http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern">
publish–subscribe pattern</a>. Open this page in several browser
windows. Each window initiates persistent
<a href="http://en.wikipedia.org/wiki/WebSocket">WebSocket</a>
connection with Mongoose, making each browser window a websocket client.
Join a room, send messages, and see messages sent by other clients.
</p>
<p>
My ID: <b><span id="my_id"></b></span>
</p>
<p>
Join room: <select id="room_sel">
<option value="">-- select room -- </option>
<option>A</option>
<option>B</option>
</select>
</p>
<div id="messages">
</div>
<p>
<input type="text" id="send_input" />
<button id="send_button">Send Message</button>
</p>
</div>
</body>
</html>
+83
View File
@@ -0,0 +1,83 @@
// Copyright (c) 2013-2014 Cesanta Software Limited
// $Date: 2014-09-09 17:07:55 UTC $
#include <string.h>
#include <time.h>
#include <signal.h>
#include <stdlib.h>
#include "mongoose.h"
static int s_signal_received = 0;
static struct mg_server *s_server = NULL;
// Data associated with each websocket connection
struct conn_data {
int room;
};
static void signal_handler(int sig_num) {
signal(sig_num, signal_handler); // Reinstantiate signal handler
s_signal_received = sig_num;
}
static void handle_websocket_message(struct mg_connection *conn) {
struct conn_data *d = (struct conn_data *) conn->connection_param;
struct mg_connection *c;
printf("[%.*s]\n", (int) conn->content_len, conn->content);
if (conn->content_len > 5 && !memcmp(conn->content, "join ", 5)) {
// Client joined new room
d->room = conn->content[5];
} else if (conn->content_len > 4 && !memcmp(conn->content, "msg ", 4) &&
d->room != 0 && d->room != '?') {
// Client has sent a message. Push this message to all clients
// that are subscribed to the same room as client
for (c = mg_next(s_server, NULL); c != NULL; c = mg_next(s_server, c)) {
struct conn_data *d2 = (struct conn_data *) c->connection_param;
if (!c->is_websocket || d2->room != d->room) continue;
mg_websocket_printf(c, WEBSOCKET_OPCODE_TEXT, "msg %c %p %.*s",
(char) d->room, conn,
conn->content_len - 4, conn->content + 4);
}
}
}
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
switch (ev) {
case MG_REQUEST:
if (conn->is_websocket) {
handle_websocket_message(conn);
return MG_TRUE;
} else {
mg_send_file(conn, "index.html", NULL); // Return MG_MORE after!
return MG_MORE;
}
case MG_WS_CONNECT:
// New websocket connection. Send connection ID back to the client.
conn->connection_param = calloc(1, sizeof(struct conn_data));
mg_websocket_printf(conn, WEBSOCKET_OPCODE_TEXT, "id %p", conn);
return MG_FALSE;
case MG_CLOSE:
free(conn->connection_param);
return MG_TRUE;
case MG_AUTH:
return MG_TRUE;
default:
return MG_FALSE;
}
}
int main(void) {
s_server = mg_create_server(NULL, ev_handler);
mg_set_option(s_server, "listening_port", "8080");
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
printf("Started on port %s\n", mg_get_option(s_server, "listening_port"));
while (s_signal_received == 0) {
mg_poll_server(s_server, 100);
}
mg_destroy_server(&s_server);
return 0;
}
+12
View File
@@ -0,0 +1,12 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = websocket_echo_server
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
SOURCES = $(PROG).c ../../mongoose.c
$(PROG): $(SOURCES)
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
@@ -30,7 +30,7 @@
out('<span style="color: blue;">RESPONSE: ' + ev.data + ' </span>');
num_messages++;
}
if (num_messages > 15) {
if (num_messages > 3) {
websocket.send('exit');
}
};
@@ -0,0 +1,61 @@
// Copyright (c) 2013-2014 Cesanta Software Limited
// $Date: 2014-09-09 17:07:55 UTC $
#include <string.h>
#include <time.h>
#include "mongoose.h"
static void push_message(struct mg_server *server, time_t current_time) {
struct mg_connection *c;
char buf[20];
int len = sprintf(buf, "%lu", (unsigned long) current_time);
// Iterate over all connections, and push current time message to websocket ones.
for (c = mg_next(server, NULL); c != NULL; c = mg_next(server, c)) {
if (c->is_websocket) {
mg_websocket_write(c, 1, buf, len);
}
}
}
static int send_reply(struct mg_connection *conn) {
if (conn->is_websocket) {
// This handler is called for each incoming websocket frame, one or more
// times for connection lifetime.
// Echo websocket data back to the client.
mg_websocket_write(conn, 1, conn->content, conn->content_len);
return conn->content_len == 4 && !memcmp(conn->content, "exit", 4) ?
MG_FALSE : MG_TRUE;
} else {
mg_send_file(conn, "index.html", NULL);
return MG_MORE;
}
}
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
switch (ev) {
case MG_AUTH: return MG_TRUE;
case MG_REQUEST: return send_reply(conn);
default: return MG_FALSE;
}
}
int main(void) {
struct mg_server *server = mg_create_server(NULL, ev_handler);
time_t current_timer = 0, last_timer = time(NULL);
mg_set_option(server, "listening_port", "8080");
printf("Started on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 100);
current_timer = time(NULL);
if (current_timer - last_timer > 0) {
last_timer = current_timer;
push_message(server, current_timer);
}
}
mg_destroy_server(&server);
return 0;
}
+15
View File
@@ -0,0 +1,15 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = ws_ssl
CFLAGS = -W -Wall -I../.. -I. -pthread -g -O0 $(CFLAGS_EXTRA)
SOURCES = ws_ssl.c ../../mongoose.c ssl_wrapper.c
all: $(PROG)
$(PROG): $(SOURCES)
$(CC) -o $(PROG) $(SOURCES) \
-DNS_ENABLE_SSL -DSSL_WRAPPER_USE_AS_LIBRARY -lssl $(CFLAGS)
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
@@ -0,0 +1,49 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwizPnrCx+/kPSdEeSJFLDXrBH+cSQsSLrCm99G1hCjzqSlIk
1BhkZMEHxBaiVLky4+M/nwhjwwRHI10h6U2Or3tbPLv7z94cPf+uCx1aF7TE3Fm3
6YnDk+CjrYVFN5GRPGOPPdFxGoc+vFvQJyAAimvchnS1ZoEQFvermwzOnKspA6gc
Px+7wnOeju9TyJuDr5ngtDXFnkcpkBNPxz3En4MJY4xJiaueafh9pIES2vSl7uP0
J/qot9v2rdiL7nt1H1vwseeEkZhQ+NLB5e2z4psyktJcwDX7wQ6j7JnKfHeP+ixO
TUORgV4foBMVOqo//Guo92Q5HoLNK77V0y4+ZQIDAQABAoIBAGEsx+LlDs3JQQty
KjOq8uKWElyC6bKcZkIMydGvg6b6AU6ceW3jnyqFJ/vMUAUSghNmQQq3yiVo2Kks
DLKTa9sKYwisE0NeJsgoUtOhJttCTlrwU4f+t/AjtgY68f7zTLnqIV+Ql4ftM0pU
sIFEFMExZbWsZrQb1w+Hd0wrRqNEbSOfSjHeigvuw1T3GH2tSBUTGTpcoewCzy7U
PKS5pkYyiKySQQNqZTac3NHPjxdK6xxzwURZp1irKdiPdt04KHLVLX8KXelt/J0k
AeYkVbpFIeQ9rNBerMEp6uRBt+nE5mvP+xx1XPqKRxuxbMyTnBXeOM2zS/a/dBiz
fwokwcECgYEA9RSsv9AQ/AR8tt+aPEQvjhJ5pn/YbCb1DA9IDXpaq3tzacGd8JHj
3kUtb79nosu85LvSkAYmtzgfJs6xZyUkscra6q+xlsJ12QRxLzqfxcp9Y0wsdqM4
AcOwuiPKrjkWxOQpyWPWRwbmAefLfRMekHt4Y/QY0CwhslpnsOsj3O0CgYEAytOE
8I4GBfSQfSjXwfrso++Oi75VSsl5ZeiMGihfEhYFTE8/3rEZf7nf9iFSkN3TT+7f
pFqQzddzPBZXlpVM6k1jcEjdpJizbeR8DmICpABFrZvKz1o8pQ2Yw+FYI86ih0x4
806snMNgg/RgcVijXKFrC5joJOI+DVgwWoQyMFkCgYBxt4MkiV2oIkjf7ca6GgVa
zbXGjOGV5Umkq96J6nDxyplVw/IN8xOhScX4aP6kahaep4vfKguCzjaeIh/stS5e
lLqZVKZ5Roe6B7ag7HnAI+GkVm73KWrOXse8xui/iFvJRfkhqgJ9+HR3A9/GjD2N
Ws0Uy+lLhn6oLAya6bA9TQKBgAVfZP4aRP6TY+Bs3Io+41XUWqpI+GlqvNR+PHfU
6e/ItYs37jEv78T6X3xdlZpQxfAwG6x22a8aLetBjEBo5Aiw1Bl9VKGvidE3ZDHd
VsSRXUckAVNMyJ52pb1KktMf/h4nYGzRgLEGW+Ai8QsPlgQ2ImfEPSH8/DfORjmf
ltTBAoGBAMxIZ52DrJvuxogSOfA1MoCD6a90trkXCquvi+A/fXojZ8BHmMQshvhK
rAO7SDIV1i1Nh3jQ/oFWE8KOprqrOLO6jNTyF65vh+zk7ztGsEME9FkDhHasUiXf
t5PE9KeTChHRvIa4FGCl9We9GftE5Ii77LWMOIq22pyxYbvHQFEf
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDlDCCAnygAwIBAgIJAIOoO+AapJ5WMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNV
BAMTA3dzMTEMMAoGA1UEChMDd3MxMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVi
bGluMB4XDTE0MDgwMzA5MTU0NVoXDTI0MDczMTA5MTU0NVowOjEMMAoGA1UEAxMD
d3MxMQwwCgYDVQQKEwN3czExCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZEdWJsaW4w
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCLM+esLH7+Q9J0R5IkUsN
esEf5xJCxIusKb30bWEKPOpKUiTUGGRkwQfEFqJUuTLj4z+fCGPDBEcjXSHpTY6v
e1s8u/vP3hw9/64LHVoXtMTcWbfpicOT4KOthUU3kZE8Y4890XEahz68W9AnIACK
a9yGdLVmgRAW96ubDM6cqykDqBw/H7vCc56O71PIm4OvmeC0NcWeRymQE0/HPcSf
gwljjEmJq55p+H2kgRLa9KXu4/Qn+qi32/at2Ivue3UfW/Cx54SRmFD40sHl7bPi
mzKS0lzANfvBDqPsmcp8d4/6LE5NQ5GBXh+gExU6qj/8a6j3ZDkegs0rvtXTLj5l
AgMBAAGjgZwwgZkwHQYDVR0OBBYEFL54xAgtJTW6US4Mbr4QG0yKzvaxMGoGA1Ud
IwRjMGGAFL54xAgtJTW6US4Mbr4QG0yKzvaxoT6kPDA6MQwwCgYDVQQDEwN3czEx
DDAKBgNVBAoTA3dzMTELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpboIJAIOo
O+AapJ5WMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAJz/RzMa9Wa2
eEXed7ijH1gcWtgVsVT1xZo0ksFl+QJ5Be1AJpOIe8nKdzYjxPWUkofIoaGHdMLL
Uc/udRzsXncup+0mD+Yos6Cqyo9yHq7L1HbXfKYZtBXIjWHdF2+RP8j9tHfITXYI
Pb2zsQ+A6PYpp5OLGZTDAnI2qffqsmwXFNhPfFhOANrGlOjsvy1P7JDzvymj/90m
NomlO3vjxLHOf6MvedTgCB0dRcAoUWPgbxPWifjBmGBjQjA4ukMQ58wbBQgvIoCW
obrXmLCNZIkpWTw4gMRYquY880IYK/OuFNJH/dawxx/WzuVr7IdLmbFY15zf5TUb
ZpIpwqRCysg=
-----END CERTIFICATE-----
@@ -0,0 +1,45 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAwOp1RS9RnE8L5TszDPIOmpf/1pqb+ki99l/sGhqB/KZBKCuq
uoCc2VPK3PCByBE15/xJ7t691FnJfForI9DO5p2R0FPD6o357hqRsh0dJNBm0VgG
iNtLQ8lyYoE72HJbkgCAUZW4N0OBibOmvp/s3Fmr7rEjW5LmZxOtX+iULKIUZ0w6
gJlM8G5QA1SuqndN0PbPx+RZKSXZCoJj+Nboqw03nyJUexzs+lynR9ITMziUaaVM
4rzG+P6joQAnUkDNydxo/d4tj4xaioZdKxWLYEbj2BUtOlJJydotWo2wcG85ONrT
Gw0ltR1vku1hidMm2QL30uGIL5SeqyE8TqWk4QIDAQABAoIBAQCxHtKauc45MA4g
4hCGAzvLTmETnRI2YlEfAoTYlpvf5pkOE8GFyI25r4gjACJ4GO0gWG9dBF7Pt7wZ
EwRmttEvxV3aIv5OvRnKNdSs7rQSV9D+xc4CGy1oSG1f6X2TxbMzQoiN32OqQa2O
S0Z94IFs8lu8JCDtc9tcqiFVXEmnC3RvJZOShWpsCsbmh5ue1Xed0MQQ47vt7Zt7
I0cutvwSFJMsZkZUJp5+KjVNYo9TEJxVD3m2NJNJxBfBoRVHXNii3hUEHcTIdIAz
omtRwBU8AKgJirGIRo1h2ZFyubI8ScGOJWIiWMQvQqTHKiOaz3yUar1NSG+kFn0U
cj7s3FhdAoGBAOQbx8Jwhtp4iOkP6aW1nVTgiaTj4LMlyJZioFwgPFRIcA3oRHt9
5SRetmgFZNvcuNw1udfeaObKmlzxwUruprwOpihgAQWJFTtOjQNrt2gsYuX4l3W6
T46dO2W1pV+mW4A5gt0aqhLv7pCS4lpreNAqyHSPqcQWcCeiTzmp/LfDAoGBANiB
FQOTyMElR9OzKwmcGfIvnhUfJQwi5qNE3R+xXiP5x36a9HQBey5ri2lnBle0Ksr/
G+RKoflmk1eFXVHN0w35yw0dVco//WE4vOknldNEnIT85k02ld8lDTa2Q/EJZtPH
un6zeU0Q2/4SZ/GXPssEZPlpPM7WtQzztlH3+sqLAoGBAKnhppvAgi4ippQsLa4j
29BiiSAsNiQ1d3XIbfUubL+4UvuIh7gQwp6biu1dVwgHEgWuXYHPOgDn0p51zaao
pbRYlJZtKVWeChnpHkv15NnIdL8grGwZHTbxElNlPIxHsM2GB1fzi8YeumUhf0In
2AnwUum8NIq8yzo5PxeK6ZNRAoGBAIEA2Q6ankJH/nZsCbbeJq+iI+Wd+ysyGI8s
Vz2tJ9Tz3iTYG9SLlWRhfF4/nw3fMqhmPa5Xsg+zSRQbSTGXHKz1LEISOq4aVtX5
QscCaUnLVh//uRJE9iRSJX92NyGGYpjKJ5ubQSnkY9EOEpVnc2jwo2HhjPQKBzNC
fF53Dh5lAoGALwTN5uxrBZLPu4DtZkOosKkv4l+kzFoOjLJR4vA7ONBx2CSe9G7F
tSsH7lZS3b0mxBWjO90WhaSvtMWWrfqq8vrqmoTE795fYxNoLfCLK13W31aTDUsI
pQRJIL30MPvASbcFHN2MD2dXz2nQzY8C9lvtvap/krYiDKDU2L7+iP8=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIC7DCCAdQCBRQHBXNHMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNVBAMTA3dzMTEM
MAoGA1UEChMDd3MxMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVibGluMB4XDTE0
MDgwMzA5MTU0NVoXDTI0MDczMTA5MTU0NVowOjEMMAoGA1UEAxMDd3MxMQwwCgYD
VQQKEwN3czExCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZHYWx3YXkwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDA6nVFL1GcTwvlOzMM8g6al//Wmpv6SL32
X+waGoH8pkEoK6q6gJzZU8rc8IHIETXn/Enu3r3UWcl8Wisj0M7mnZHQU8Pqjfnu
GpGyHR0k0GbRWAaI20tDyXJigTvYcluSAIBRlbg3Q4GJs6a+n+zcWavusSNbkuZn
E61f6JQsohRnTDqAmUzwblADVK6qd03Q9s/H5FkpJdkKgmP41uirDTefIlR7HOz6
XKdH0hMzOJRppUzivMb4/qOhACdSQM3J3Gj93i2PjFqKhl0rFYtgRuPYFS06UknJ
2i1ajbBwbzk42tMbDSW1HW+S7WGJ0ybZAvfS4YgvlJ6rITxOpaThAgMBAAEwDQYJ
KoZIhvcNAQEFBQADggEBABPLmq6zKOMY0WRjtBoSymq6f+vXeEwtWCfVejdG6RlG
/PTdCKNvp3OL7FDnmQQ+r5rMs4+Os4fX/g315QFKXu01rqxmFb2XVNhhaECdUWtK
QP6ZoVZviUiDjhK6a+05aerPCJpkGy/lz0W6gmj4qhuAQbobxb6UbzqTRYY+ZwGk
+SI3TAVCdmXFlxN/M9b0DbmkseRG8GGFmyRYyRb84vbV6zemFI++5ROUT9zXT7ey
nYfFJvAAk5jJhY5UP2aMlVWYYa4jUZrrPLoiBLUuRrp67EKGebCH9mgCIf8ztNJF
fpuvcz++LUeRyTlAGDefe+FyHGIwFzIfZn39D7CaRvM=
-----END CERTIFICATE-----
@@ -0,0 +1,45 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAyal0BZKRYd+Wpxv4MB8LjjgXv/MxSN5oSAKThlCZ/AWG0FEP
d4nrBACT2xUxwo+xbYl3joiwL/eCPAp6QNKRGhvXVOnSIFVSjKZWbdX+toqK9pDS
QMDTL4ZJvK6pLZXknyHjEr0PxZh22F7iS1+C8HxBPj0Xgg/u5/+jPhFPPZ1d5elv
4cm/z+xy6RjFlA80aIeK7dcWssOsOIPjUNFfmoYgR63ScZIlUZj6j8VX9oX7fJID
jumGajDxgD2nBWFbHcGKin6bz/wZ+OIhXOCDdY7oKuMW4JiBwbfBtedkQuQYS11s
PRFFYDLoZH59Ivcu0c4F2tomE86qM8THsI910wIDAQABAoIBAG55FAQRfO8/C0rU
eavy9eOdOvV+hltC66G3N5X3BcQYSvhHz89OkJ6KqnT0MWRCT5KQIhzFKK++SWwW
2U41jCPfaKEtzlzEIQrH/MUC3Byn3OSiBWxPteFtEWv5ytgcKzg52iljxQYcNc7m
e9WKpzKS/zLXSM+JZvlVA9p2pRA88kUZ/EE5H+FW3kHj5eGNqX+cxUVpL0VKTiLv
aXWjYotpmDJW/Rn9wRQethm6Gdx3bvo+LEVlJRELNq8NM5H/tZIVRzudJOgzsw5v
3OQGhfKB6Eg/vqSFoZxX6ApXDxmtaSO83B0kK550bDVv4sBnOExGjKCzybt04tet
KtLPPoECgYEA5WUD+mFL99sCX6pzIyUVlxp9eUhVy5nvhoF6u3mvhay2XsZUY0wy
+/qVqYSZTvuvJ6JSXc4iVIX8u/gj7914805AwujepIF/8E0AaXLBMndzDE4ze5S5
2RHI7Cy4/3AWOcQ9wFFmUdSs7/6oAkcQtvzP40hGg3J2jAEhIdCqmbMCgYEA4Q0G
BYP9XeTdh0C+BcP9B5VUEC0jerYS8VqVqriB+9JfT3InI7K08sOG2DiQQBhAHuzL
RhCECU2a9j0+u5F5JNeY5m3IhU73Lw+lOlUkMaAO6x7JJEzhXhonE7Kv8fWygr/0
OB7yzqz+YsWdQ2VOPZ88ntlAYE65vzcaVswZY2ECgYEAr7Gt2VA6Ei0A5Wq0Yr+d
iKz2WzUG2TkelqOG8B4kTDrbNz2qFp+fERV9GWgAz9i+75lIgqZF7vzsdL96LtYv
NBLEUURwegjhh5hCb4E/7bpFOLCQh9+CdHpFrHYYfzRHIZlnPmxZ9OTyS6J85bmu
WKjLRKXvs++wUkzvJmoesDcCgYEAkTOB6xUZ5/a+J4HSGI43NylVr4owFgBbgHVd
k2SwGPXGoM+aCSJINUmKOv9jsrbyyAEntfD5/7aegLlLPGHDs82WzTWP5tLoEOkb
ReOhEpOejHy0ckNYNQrSo5bqhkZsAogu3fa52jcrejbeHJnEPWX8CtFJA9pHZeP7
jnzo9IECgYBefHg0dymSj2xxN0XmC+S5cvQu2K/NYUpatoWvHnPiQ87wIM0AWz0O
D0ghEI+Ze57NbtSrrcTE7hY/LHrAqXGAB9XNIM5g9Pp/lM+XjzKVr1FMf4xpuHf1
VJJRHrOU14CvMvKbgbPrL6B0d5yrYmeex7GxNw0ZVvtjCa502Eck+w==
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIC7DCCAdQCBRQHBXNGMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNVBAMTA3dzMTEM
MAoGA1UEChMDd3MxMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVibGluMB4XDTE0
MDgwMzA5MTU0NVoXDTI0MDczMTA5MTU0NVowOjEMMAoGA1UEAxMDd3MxMQwwCgYD
VQQKEwN3czExCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZHYWx3YXkwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJqXQFkpFh35anG/gwHwuOOBe/8zFI3mhI
ApOGUJn8BYbQUQ93iesEAJPbFTHCj7FtiXeOiLAv94I8CnpA0pEaG9dU6dIgVVKM
plZt1f62ior2kNJAwNMvhkm8rqktleSfIeMSvQ/FmHbYXuJLX4LwfEE+PReCD+7n
/6M+EU89nV3l6W/hyb/P7HLpGMWUDzRoh4rt1xayw6w4g+NQ0V+ahiBHrdJxkiVR
mPqPxVf2hft8kgOO6YZqMPGAPacFYVsdwYqKfpvP/Bn44iFc4IN1jugq4xbgmIHB
t8G152RC5BhLXWw9EUVgMuhkfn0i9y7RzgXa2iYTzqozxMewj3XTAgMBAAEwDQYJ
KoZIhvcNAQEFBQADggEBAE20gAykuuaCoP49GnZ/Z6ZItFry4Fl6iCWBDdEsWI9R
wRNYumeaeejdFPXfSJdTT7UlrVK1WWGLQLq+ixHRDX+V9T67ou85F92H/OxbUoPr
iz/TZAEBTC1GvTJl49lsfPl1dTWH8T4Ej2hxCUvIJrkCkI2Ov4Wwef6A26USrwBt
S/CPInjCe6qkE5E8xfTDl8k5IIgMadTPhi5sbV2piBJoN4floJPqR0hdDKbgUymn
5WNSiRkuI6UIDZwQEp+A8TmFBHbSwfTGt2Sz5iI27P8J6pFvR5eRA1k57dRUWNXC
WAU1nqteP3QAjj9L3o8IO0T62scaiJX8x01gTmVVe2I=
-----END CERTIFICATE-----
@@ -0,0 +1,49 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwXIMpHuTNsro2pxe6y1mu27md2Olhvfx26rO3maO0/stIC2z
G/xQatFDLIWzfKFYOT0iSEj252ENFDCw6aDRKpiaUFtXcMAWkNkkKntEyoEgE45k
rTrvpay0v0B+ojwjA1Jz/9v35cgvDwTs3vNFno5HhI0m2YF4ocTmeHJ6u0xRL/qy
atfKsfuVq5s5CXOYCXp3Ux6kJ1c22J0EdZMvS75SVjAZgRkqQpqt9L3e2ZBCEgUr
w0KwlERvpeJF+sJJOshXjfrDzvwL8IpPnGZLJNINFbSJMk5MFGcMyq/28pSZLB9E
Dh8vk5D5gUnxM60ONUy2nYPcYr5p1PLDiC8hfQIDAQABAoIBAB0Twpi6xn8W8vdh
R9c75NRJsDTD8q6d+GnXe+7sJY3xlG/gzqpnO8NCn0FC+57BNdystsl8xjgzW17s
jrsfZDFt7MwlXrhg90NgkFIeY1G5JRQrdDChykHx+t1AmYhTV8P5EdykuNd+RqyQ
RfahRJa3tkJTYUKSdoqCaU4zjwU2CSxltuJx24V+WoZE12EwewJ8HPg2XTnbsGE7
Fnx5s29O4ItM70CC0536AY/OgfuPix5z573VQiilqqfOQkVkKa1fHd6tGpWU+3kH
X9FnhEBf9cN9tVgmaB0sCSVVrfgqSXg1EwKHqe/+FCumuesA68Q35+/K3b+QrNiR
ka2yliECgYEA+V/4pbgG/lPYvTwWhKxGXXdJmrSPgZC0mwE+fRuYkeptbIkS0pwt
/UDTXk9nttj1f1ZJ3NgQbT/1w8jpXfsCJ8VeGzL9+ADhRKWVFRlu/nyFCMXawLEV
rot7SEr8BW/m8moHgY5lYipM3dXJPk0F+KLrN60U/aNmFUtPGW802BkCgYEAxpWy
FGL2sEQ0QaRDTcqqF5faVqw+5rVGvN+EX5o26E0QWWnoo3L2c2/5X93iBl+Fqtnm
9jSIQUC3rYOawKnZ/HcIE2ergFit/p6JaV9NiLDRtDUmSzlffEGVCj0neYFsnWp5
zcmkUyZ6fr19EmKQWfdteNBlXue32TjVlFbfUQUCgYAfMbgi0sBdNBPaqBeRBRPQ
QUm9xnRlGrrc4Oz2LWuKZS7G8uad3deK5H8MPxaUMtOS2DJpI8X6RJPzp8A5d1qv
quq4sEpAqauEMMpTV1khEGZ70HQqwnwZ12zWgDrCW1siW80QkcVw4CW5YjLITk4+
6fJOhqInkDcG1uLQJa8QkQKBgQCfs8l4DbJ4RRGFbLXXvNGXkb68j18yqLxPrq3F
OL9JiJhKYBsAP7clVPrG9ykLmQxlP0I35D1jxMkymLD+mlo9Z/itqmTJHggnyZWW
kVdIQ3MSKuA2BNjek9tpVY8Gb2hLHFMChVRKrpo6jOclvvB5+bsnOukbLtyyq7tP
xaFohQKBgByCmlltjOBWZLFLeA1x8j3inm9zM/FAJuANbHUOZ1RwrRcNFbDv/FXm
rLPnPCaH5AwAWhVRJcNHo37Ee0s/xqe+Q4dG4xL943k+6KlopAw1SXhuXF6PnBfF
y+ArVlh9d2oWN5cBEzRddnWnKJuMi70kMzYf6dIW9s/dHbq/gFDy
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDlDCCAnygAwIBAgIJAJDtcXU2wiJIMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNV
BAMTA3dzMjEMMAoGA1UEChMDd3MyMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVi
bGluMB4XDTE0MDgwMzA5MTU0OFoXDTI0MDczMTA5MTU0OFowOjEMMAoGA1UEAxMD
d3MyMQwwCgYDVQQKEwN3czIxCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZEdWJsaW4w
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBcgyke5M2yujanF7rLWa7
buZ3Y6WG9/Hbqs7eZo7T+y0gLbMb/FBq0UMshbN8oVg5PSJISPbnYQ0UMLDpoNEq
mJpQW1dwwBaQ2SQqe0TKgSATjmStOu+lrLS/QH6iPCMDUnP/2/flyC8PBOze80We
jkeEjSbZgXihxOZ4cnq7TFEv+rJq18qx+5WrmzkJc5gJendTHqQnVzbYnQR1ky9L
vlJWMBmBGSpCmq30vd7ZkEISBSvDQrCURG+l4kX6wkk6yFeN+sPO/Avwik+cZksk
0g0VtIkyTkwUZwzKr/bylJksH0QOHy+TkPmBSfEzrQ41TLadg9xivmnU8sOILyF9
AgMBAAGjgZwwgZkwHQYDVR0OBBYEFLK4flD5QD/mRufsPx63xlEKM8pwMGoGA1Ud
IwRjMGGAFLK4flD5QD/mRufsPx63xlEKM8pwoT6kPDA6MQwwCgYDVQQDEwN3czIx
DDAKBgNVBAoTA3dzMjELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpboIJAJDt
cXU2wiJIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAEYD+CReikYr
Rzvk+/Vdi/7IcaH9CFknIdtineSIw1y98nxnbnNJqxwfNaRblbYvg6OFdUI3POuI
+rdYLCFl8z3tWqRHLkGqHSJA9xcng3jLJxz0+ctiVcekJvXaB3O6eSZjhGbmmI/s
CQhdy2zpEIVOeUq50DrSJp9CknyGu/IkaGx5GOZtkiHMrpig50CRjX1lS6qrMNYp
vB8gfuqpjsL4Ar3vg+lgMSwNWXBNHrIRPHB5VEzBEdmLFZlvueR0ooEMCklpwX/a
lFImVc6JcY1pBEkHTiTLGMpGAHG3I1aVUaWb3L+V+2ym/KNRNL5C2+1eiqql5u8m
HUaOcNC90ew=
-----END CERTIFICATE-----
@@ -0,0 +1,45 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA0ucemqFwBFziVOgTgx4mZII4WnGDpA/rWAGHvUZOqy2dQ3Pz
woGKxBaVPAl5kxEosROVGa7dTuq5yFZ4XIGvwCKxF30vCmdGCytqq6MMp904jG32
KikkmjCApIMGxMO4eoBHZZxiyVvKTbg9M2CRXErwnYWhFH/qGdPnuo0CEaHJA4VK
A9incT9dpeEhU30R6ajAe/je9rCj2OMLMFSMfd51L/VYfO60zDwUNY7YVIghQZgJ
e44EVGsp1pXaqaD6o3PvGY3ohw2aZjJzlJ7MJHbKV9lft98R3pklbpBzMH849cEy
Q/51L/rlfTUgCpTy7wEZpWHQNtHfu/1rhJjpNQIDAQABAoIBAQCUNIHXG/dBuZv7
GpMLotZL7w520Co30lAJqhmfMpb5x7YpvoPffXTsUwpQBECAzqAPv7kZMT6nxF8F
n245Y5EDrd1QqlGyN9yK4Nm2/39XPygL1wITopHsIIVmFgVdpEQxIZAKoZjx8yT4
9K1dO1Eq0CbCKzOE2lbCC51eBNUdWZIMxwC6O/j/KoIkZ/HwlG2hpUuXg8x/XawA
ZJDCoTcWHCjYP10FxKVN3vAyWM2IM44o9IbsAGEOswR4gUwRsgq6Ehc1U59XUHi+
x30oda55I1/8xD+SfP/zk2dDPHkv/hq5+258GU/THsw2+AAexocvSIS/g9EppTEg
biFaDKzJAoGBAPqey10JeyiOlHbBjoSSa7lJYUjocQANFQ4ayOAgfNX72iyabrKF
p9sVAeO/nM00+DPrm2wWws03ScsPeh+9BDJMPRBUHfSNp7+F+oyj7PWHBEFHbyO9
5HnYZP+1vhdG2dYPIY2gRSFXpiGn3j0M1D0w9c7Ilm8ee3krdR4E/mw3AoGBANdu
EfS1dK3+m7sEgc2+3U32z83GpuCHeUIKGPYMLI0fIb1CPpboHU9YjOFJZH9iIIdl
00JC963O3+pqLe0XbMOmBVt9QjZfhfB+AY+JHtbPgoQVLtq9X/WvW7h3xn6S971r
Crkhqay3Cs4BzsgYDYraQCTw3oq4twR9Nuy4etfzAoGAXOsG5wWe3diO/sCggFJx
Eg88vHVBgA1ZoxMXKtGgtw1bRHI1XIblRvqw6qmeDw72fvl5dEe0DbXT7C9ezemc
ZrGRaj5lpMfoS7/2trIIJrfaQgGkGRJMZUhvmcbeJW8lUJHnlMS5HLWMaKn+YZAi
GFXQrMv9ylD44mHUWD7tvV0CgYBNctPfvvCQsQ05ofgsiKa1Jbs1hmpuJCYy2MB6
jIvjvEJ78PnhdNc8tGAJikIoDZYWN0RI+RxkDxCvDLcwGpDOkbwxVQnd1F+pwxM6
kBhXL8kDRT5QA28hO4bk/aKN1LZeEcKMJg8C+ddXkozNoOAVgDs5TKMlCh057u41
EmmPgwKBgQDOlYi7fPYOCy0yjHMxSrp2SZOS06AMWGbbCoGkjRtvuP+FmKSNB+LZ
pOSEPJgzjsRutKjneww4LpV6dViAyTcP5JoeQpokHf7UVo7yq2QH/iwF3zJwsC/S
OuVLkqpZzWuye/QCH5NOTfw27ye8jG8VcQW2QPbcbkLXLM7zg2yX7g==
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIC7DCCAdQCBRQHBXNJMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNVBAMTA3dzMjEM
MAoGA1UEChMDd3MyMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVibGluMB4XDTE0
MDgwMzA5MTU0OFoXDTI0MDczMTA5MTU0OFowOjEMMAoGA1UEAxMDd3MyMQwwCgYD
VQQKEwN3czIxCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZHYWx3YXkwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDS5x6aoXAEXOJU6BODHiZkgjhacYOkD+tY
AYe9Rk6rLZ1Dc/PCgYrEFpU8CXmTESixE5UZrt1O6rnIVnhcga/AIrEXfS8KZ0YL
K2qrowyn3TiMbfYqKSSaMICkgwbEw7h6gEdlnGLJW8pNuD0zYJFcSvCdhaEUf+oZ
0+e6jQIRockDhUoD2KdxP12l4SFTfRHpqMB7+N72sKPY4wswVIx93nUv9Vh87rTM
PBQ1jthUiCFBmAl7jgRUaynWldqpoPqjc+8ZjeiHDZpmMnOUnswkdspX2V+33xHe
mSVukHMwfzj1wTJD/nUv+uV9NSAKlPLvARmlYdA20d+7/WuEmOk1AgMBAAEwDQYJ
KoZIhvcNAQEFBQADggEBACCCAJypO9DFU6GeOH+FwE0JCLTypHoIwERWxNL7xfjg
rwVqIxwAEo+fJjL+QY7JbAb/eqKaXIBYkAF2lFc4iEmecXX/A3Aqw95AYi78o7HD
MwRPqJha9mxLcCWwjX8XK8pT152BvYFPNhi+6jd++rDRxKDfmNvgdUQ2/YW6a5Wv
zEmLDPUWRIuMQIEmOa2/JhlllDviMExTw51nbqYgCghycRvDACyQAuu8re7P6gcg
bXObNlfxcU/8Ph6MFI+2S9ODtQ4BHyuKd4kRNsYn8vV42a0h3bCYSGPk3kSIgxd7
XijwHT/o8E9ddH2BvDv+6Nhno9C6/MbezEOIs4nlhRk=
-----END CERTIFICATE-----
@@ -0,0 +1,45 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA29iFnMf4pjty6knOt4wT0joPUlU2dGCFWxQ5Eg77Yx3Lou2a
FOzNGp7zLYH8gauOK+mgY+B9cBv8PvVtUQQKB0SKTTV8ZNKVyP3O5F2gRSJ+tHtT
FqaPfG0WPOn/f02YbOpAe6Rk+NnlJoeuBxG4uapUSq7r0mTGJQe+52y1LcMs23ID
ENBfDoUMVt5vCnF+cYK5ndeFVLyPO3JjohDH1zv3a9ECG28rtjKHLpNYFDsPJaD7
UPuyyk3hIvfPCZoJOUlEVfpLT/lM+9oCPnq9PwIp5NqYofkuc3eKooCo7N4r4IlP
Ajktaao6b0ScNwNQB3leOIdEUFSIYy8N1JszVwIDAQABAoIBAFlIvjrGG/2m9yyf
fQyeHw6p9b8CTHNHH+G1fNgQrZe7ahBpXsJQyZueIjTBLcOb4MmEwFbPvSHiu7b2
Bcd5VHlPJLvmlPZ9b8eJDJVCUOzC7aJu03fHfU6THwzuG42f/d9942JTiY5nL+FO
CSdl0xfUTRdnou53buFrG+TxCUPj13HP1HY6DAVzEIq1H4TZwIZo7KRRTIYpTB3P
6yvr9xsISLlnmfQ4tp2pApl5o+bHJEhr1VO6SAT/pSyShi79KmMMqYtyTmOMz7w6
VJkre5ybnXBDN6tfMHWqdobJ4gRWK9rqf+LIZig5XQnyzkue8k+I7aPgO4xNFh56
dkejQcECgYEA9MDCWViqpfvof+epiKzccqnIRnz1EfHdRQjiKsKGRe39+K+pyaqJ
FOOPFy3aOw6M4cFWwcdMYzKTItvzyLVhDqMzT5eup/NVqo5tPoy93XPf2qRYiTl4
2j5wvm0RVkYEONd3pk2lbfbUmn7XQXj0+AG60SvsqErF/UhIBGec/xsCgYEA5fLC
EdiiC98kr4sVaE8G854WI+aAkStqO5YXyrnsMzRsqk8KVVYE1yCC9rYyymDBYmlx
uEW+vbWqLc8PO3v4ty3o5ff303lPMWIrvKiUldjqMjS6ncWxsQjU0bygGVgOgHO7
c8rjiDH5M0JgWSREYUVFT5mW/5+Y1LVT8mYNlHUCgYEAhMSX6N23XGkFW3Twu2qB
/1Vohgw86OoaDNvfzDBPpFmQ3rlz0ijHSeSTd5BxBH5FICXACUgygNErjcphOSxj
JQyUxgVTQlo2y1mNm1O/nwS/lxx1xqK9ky4x/Kqvr+w1WBxSFI2kQr2V4OUTobma
sXpGvDcmnrhJJLd0EaefO6cCgYEA3Xw/S9tC8nZjqqYn34nHI16Q6tF54tpTf8Np
dT4x8Xw8cqqhRGMPVHsfSi1irKYXfwgbnienuqlBmtAHVv9pKF+TJfb7gXkmO2XY
xOYIAHGn2uYJHjCun9vmyYKLHv4/MaDH3Jd/I88mviXgEdyp9Js5UJua4utB1Rg3
HJMJ34UCgYEAr0PpHEBMbZXZBybNU96+jRTgkrNeJpzlnMy7et2IsRAtLjZ0mpbn
NaX8i8eO+ubweqFdhOvbh7Hd0zr7BzrYcUG1e3njhtxJE1MgWL5plnLVUbIyDAm3
iBpIHIBASNCN3sqeq+VqXvavRmeZh5O0vyLP46/kxZx0rzR/NCi9xxU=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIC7DCCAdQCBRQHBXNIMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNVBAMTA3dzMjEM
MAoGA1UEChMDd3MyMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVibGluMB4XDTE0
MDgwMzA5MTU0OFoXDTI0MDczMTA5MTU0OFowOjEMMAoGA1UEAxMDd3MyMQwwCgYD
VQQKEwN3czIxCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZHYWx3YXkwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDb2IWcx/imO3LqSc63jBPSOg9SVTZ0YIVb
FDkSDvtjHcui7ZoU7M0anvMtgfyBq44r6aBj4H1wG/w+9W1RBAoHRIpNNXxk0pXI
/c7kXaBFIn60e1MWpo98bRY86f9/TZhs6kB7pGT42eUmh64HEbi5qlRKruvSZMYl
B77nbLUtwyzbcgMQ0F8OhQxW3m8KcX5xgrmd14VUvI87cmOiEMfXO/dr0QIbbyu2
Mocuk1gUOw8loPtQ+7LKTeEi988Jmgk5SURV+ktP+Uz72gI+er0/Aink2pih+S5z
d4qigKjs3ivgiU8COS1pqjpvRJw3A1AHeV44h0RQVIhjLw3UmzNXAgMBAAEwDQYJ
KoZIhvcNAQEFBQADggEBALi/RmqeXGazT/WRj9+ZqdcnbcHwK5wwr2/YkpFPJ0Hf
ZDm+2vgjDdTT6cJS6fau0M5nliYdz89aQQo1j9RSRZnzlc/2YCFXyRLCOJYaINbj
1MEUAvNDGL7xTpepK9hVkXASRkbyNXERXRKFI1N+vpKu6UorT6/osEV/qM+MFJ3s
24xE8/J3J4MirVQVt6eY6Jb+tkliOPMIugr6YQlLsqJygEWATP8Qsr81XSfcZhVq
rXzVt7QV8dO0nStMjKK5omrtEAhVnASk7w1tFHkpBF1rqXGoo9ML40RnFZ+E5zqi
iZtzp+NzzLnEnWMNs+fJpPJ96P0kbq2bQzuSBcUynq0=
-----END CERTIFICATE-----
+253
View File
@@ -0,0 +1,253 @@
// Copyright (c) 2014 Cesanta Software Limited
// All rights reserved
//
// This software is dual-licensed: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation. For the terms of this
// license, see <http://www.gnu.org/licenses/>.
//
// You are free to use this software under the terms of the GNU General
// Public License, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// Alternatively, you can license this software under a commercial
// license, as set out in <http://cesanta.com/>.
//
// $Date: 2014-09-28 05:04:41 UTC $
#ifndef NS_SKELETON_HEADER_INCLUDED
#define NS_SKELETON_HEADER_INCLUDED
#define NS_SKELETON_VERSION "2.1.0"
#undef UNICODE // Use ANSI WinAPI functions
#undef _UNICODE // Use multibyte encoding on Windows
#define _MBCS // Use multibyte encoding on Windows
#define _INTEGRAL_MAX_BITS 64 // Enable _stati64() on Windows
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005+
#undef WIN32_LEAN_AND_MEAN // Let windows.h always include winsock2.h
#define _XOPEN_SOURCE 600 // For flockfile() on Linux
#define __STDC_FORMAT_MACROS // <inttypes.h> wants this for C++
#define __STDC_LIMIT_MACROS // C++ wants that for INT64_MAX
#ifndef _LARGEFILE_SOURCE
#define _LARGEFILE_SOURCE // Enable fseeko() and ftello() functions
#endif
#define _FILE_OFFSET_BITS 64 // Enable 64-bit file offsets
#ifdef _MSC_VER
#pragma warning (disable : 4127) // FD_SET() emits warning, disable it
#pragma warning (disable : 4204) // missing c99 support
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#ifdef _WIN32
#ifdef _MSC_VER
#pragma comment(lib, "ws2_32.lib") // Linking with winsock library
#endif
#include <windows.h>
#include <process.h>
#ifndef EINPROGRESS
#define EINPROGRESS WSAEINPROGRESS
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif
#ifndef __func__
#define STRX(x) #x
#define STR(x) STRX(x)
#define __func__ __FILE__ ":" STR(__LINE__)
#endif
#ifndef va_copy
#define va_copy(x,y) x = y
#endif // MINGW #defines va_copy
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define sleep(x) Sleep((x) * 1000)
#define to64(x) _atoi64(x)
typedef int socklen_t;
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef unsigned __int64 uint64_t;
typedef __int64 int64_t;
typedef SOCKET sock_t;
typedef struct _stati64 ns_stat_t;
#ifndef S_ISDIR
#define S_ISDIR(x) ((x) & _S_IFDIR)
#endif
#else
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <pthread.h>
#include <stdarg.h>
#include <unistd.h>
#include <arpa/inet.h> // For inet_pton() when NS_ENABLE_IPV6 is defined
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/select.h>
#define closesocket(x) close(x)
#define __cdecl
#define INVALID_SOCKET (-1)
#define to64(x) strtoll(x, NULL, 10)
typedef int sock_t;
typedef struct stat ns_stat_t;
#endif
#ifdef NS_ENABLE_DEBUG
#define DBG(x) do { printf("%-20s ", __func__); printf x; putchar('\n'); \
fflush(stdout); } while(0)
#else
#define DBG(x)
#endif
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
#endif
#ifdef NS_ENABLE_SSL
#ifdef __APPLE__
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#include <openssl/ssl.h>
#else
typedef void *SSL;
typedef void *SSL_CTX;
#endif
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
union socket_address {
struct sockaddr sa;
struct sockaddr_in sin;
#ifdef NS_ENABLE_IPV6
struct sockaddr_in6 sin6;
#else
struct sockaddr sin6;
#endif
};
// Describes chunk of memory
struct ns_str {
const char *p;
size_t len;
};
// IO buffers interface
struct iobuf {
char *buf;
size_t len;
size_t size;
};
void iobuf_init(struct iobuf *, size_t initial_size);
void iobuf_free(struct iobuf *);
size_t iobuf_append(struct iobuf *, const void *data, size_t data_size);
void iobuf_remove(struct iobuf *, size_t data_size);
void iobuf_resize(struct iobuf *, size_t new_size);
// Callback function (event handler) prototype, must be defined by user.
// Net skeleton will call event handler, passing events defined above.
struct ns_connection;
typedef void (*ns_callback_t)(struct ns_connection *, int event_num, void *evp);
// Events. Meaning of event parameter (evp) is given in the comment.
#define NS_POLL 0 // Sent to each connection on each call to ns_mgr_poll()
#define NS_ACCEPT 1 // New connection accept()-ed. union socket_address *addr
#define NS_CONNECT 2 // connect() succeeded or failed. int *success_status
#define NS_RECV 3 // Data has benn received. int *num_bytes
#define NS_SEND 4 // Data has been written to a socket. int *num_bytes
#define NS_CLOSE 5 // Connection is closed. NULL
struct ns_mgr {
struct ns_connection *active_connections;
const char *hexdump_file; // Debug hexdump file path
sock_t ctl[2]; // Socketpair for mg_wakeup()
void *user_data; // User data
};
struct ns_connection {
struct ns_connection *next, *prev; // ns_mgr::active_connections linkage
struct ns_connection *listener; // Set only for accept()-ed connections
struct ns_mgr *mgr;
sock_t sock; // Socket
union socket_address sa; // Peer address
struct iobuf recv_iobuf; // Received data
struct iobuf send_iobuf; // Data scheduled for sending
SSL *ssl;
SSL_CTX *ssl_ctx;
void *user_data; // User-specific data
void *proto_data; // Application protocol-specific data
time_t last_io_time; // Timestamp of the last socket IO
ns_callback_t callback; // Event handler function
unsigned int flags;
#define NSF_FINISHED_SENDING_DATA (1 << 0)
#define NSF_BUFFER_BUT_DONT_SEND (1 << 1)
#define NSF_SSL_HANDSHAKE_DONE (1 << 2)
#define NSF_CONNECTING (1 << 3)
#define NSF_CLOSE_IMMEDIATELY (1 << 4)
#define NSF_WANT_READ (1 << 5)
#define NSF_WANT_WRITE (1 << 6)
#define NSF_LISTENING (1 << 7)
#define NSF_UDP (1 << 8)
#define NSF_USER_1 (1 << 20)
#define NSF_USER_2 (1 << 21)
#define NSF_USER_3 (1 << 22)
#define NSF_USER_4 (1 << 23)
#define NSF_USER_5 (1 << 24)
#define NSF_USER_6 (1 << 25)
};
void ns_mgr_init(struct ns_mgr *, void *user_data);
void ns_mgr_free(struct ns_mgr *);
time_t ns_mgr_poll(struct ns_mgr *, int milli);
void ns_broadcast(struct ns_mgr *, ns_callback_t, void *, size_t);
struct ns_connection *ns_next(struct ns_mgr *, struct ns_connection *);
struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t,
ns_callback_t, void *);
struct ns_connection *ns_bind(struct ns_mgr *, const char *,
ns_callback_t, void *);
struct ns_connection *ns_connect(struct ns_mgr *, const char *,
ns_callback_t, void *);
int ns_send(struct ns_connection *, const void *buf, int len);
int ns_printf(struct ns_connection *, const char *fmt, ...);
int ns_vprintf(struct ns_connection *, const char *fmt, va_list ap);
// Utility functions
void *ns_start_thread(void *(*f)(void *), void *p);
int ns_socketpair(sock_t [2]);
int ns_socketpair2(sock_t [2], int sock_type); // SOCK_STREAM or SOCK_DGRAM
void ns_set_close_on_exec(sock_t);
void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags);
int ns_hexdump(const void *buf, int len, char *dst, int dst_len);
int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap);
int ns_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // NS_SKELETON_HEADER_INCLUDED
+123
View File
@@ -0,0 +1,123 @@
// Copyright (c) 2014 Cesanta Software Limited
// All rights reserved
//
// This software is dual-licensed: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation. For the terms of this
// license, see <http://www.gnu.org/licenses/>.
//
// You are free to use this software under the terms of the GNU General
// Public License, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// Alternatively, you can license this software under a commercial
// license, as set out in <http://cesanta.com/products.html>.
//
// $Date$
#include "net_skeleton.h"
#include "ssl_wrapper.h"
static void ev_handler(struct ns_connection *nc, int ev, void *p) {
const char *target_addr = (const char *) nc->mgr->user_data;
struct ns_connection *pc = (struct ns_connection *) nc->user_data;
struct iobuf *io = &nc->recv_iobuf;
(void) p;
switch (ev) {
case NS_ACCEPT:
// Create a connection to the target, and interlink both connections
nc->user_data = ns_connect(nc->mgr, target_addr, ev_handler, nc);
if (nc->user_data == NULL) {
nc->flags |= NSF_CLOSE_IMMEDIATELY;
}
break;
case NS_CLOSE:
// If either connection closes, unlink them and shedule closing
if (pc != NULL) {
pc->flags |= NSF_FINISHED_SENDING_DATA;
pc->user_data = NULL;
}
nc->user_data = NULL;
break;
case NS_RECV:
// Forward arrived data to the other connection, and discard from buffer
if (pc != NULL) {
ns_send(pc, io->buf, io->len);
iobuf_remove(io, io->len);
}
break;
default:
break;
}
}
void *ssl_wrapper_init(const char *local_addr, const char *target_addr,
const char **err_msg) {
struct ns_mgr *mgr = (struct ns_mgr *) calloc(1, sizeof(mgr[0]));
*err_msg = NULL;
if (mgr == NULL) {
*err_msg = "malloc failed";
} else {
ns_mgr_init(mgr, (void *) target_addr);
if (ns_bind(mgr, local_addr, ev_handler, NULL) == NULL) {
*err_msg = "ns_bind() failed: bad listening_port";
ns_mgr_free(mgr);
free(mgr);
mgr = NULL;
}
}
return mgr;
}
void ssl_wrapper_serve(void *param, volatile int *quit) {
struct ns_mgr *mgr = (struct ns_mgr *) param;
while (*quit == 0) {
ns_mgr_poll(mgr, 1000);
}
ns_mgr_free(mgr);
free(mgr);
}
#ifndef SSL_WRAPPER_USE_AS_LIBRARY
static int s_received_signal = 0;
static void signal_handler(int sig_num) {
signal(sig_num, signal_handler);
s_received_signal = sig_num;
}
static void show_usage_and_exit(const char *prog) {
fprintf(stderr, "Usage: %s <listening_address> <target_address>\n", prog);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]) {
void *wrapper;
const char *err_msg;
if (argc != 3) {
show_usage_and_exit(argv[0]);
}
// Setup signal handlers
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
signal(SIGPIPE, SIG_IGN);
if ((wrapper = ssl_wrapper_init(argv[1], argv[2], &err_msg)) == NULL) {
fprintf(stderr, "Error: %s\n", err_msg);
exit(EXIT_FAILURE);
}
ssl_wrapper_serve(wrapper, &s_received_signal);
return EXIT_SUCCESS;
}
#endif // SSL_WRAPPER_USE_AS_LIBRARY
@@ -0,0 +1,34 @@
// Copyright (c) 2014 Cesanta Software Limited
// All rights reserved
//
// This software is dual-licensed: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation. For the terms of this
// license, see <http://www.gnu.org/licenses/>.
//
// You are free to use this software under the terms of the GNU General
// Public License, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// Alternatively, you can license this software under a commercial
// license, as set out in <http://cesanta.com/products.html>.
//
// $Date$
#ifndef SSL_WRAPPER_HEADER_INCLUDED
#define SSL_WRAPPER_HEADER_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void *ssl_wrapper_init(const char *listen_addr, const char *target_addr,
const char **err_msg);
void ssl_wrapper_serve(void *, volatile int *stop_marker);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // SSL_WRAPPER_HEADER_INCLUDED
+182
View File
@@ -0,0 +1,182 @@
// Copyright (c) 2014 Cesanta Software
// All rights reserved
//
// This example demostrates proxying of WebSocket traffic, regardless of the
// protocol (ws:// or wss://).
// To use this example:
// 1. configure your browser to use a proxy on port 2014
// 2. import certs/ws1_ca.pem and certs/ws2_ca.pem into the trusted
// certificates list on your browser
// 3. make && ./ws_ssl
// 4. Point your browser to http://ws_ssl.com
// A page with 4 sections should appear, showing websocket echoes
#include "net_skeleton.h"
#include "mongoose.h"
#include "ssl_wrapper.h"
#define S1_PEM "certs/ws1_server.pem"
#define C1_PEM "certs/ws1_client.pem"
#define CA1_PEM "certs/ws1_ca.pem"
#define S2_PEM "certs/ws2_server.pem"
#define C2_PEM "certs/ws2_client.pem"
#define CA2_PEM "certs/ws2_ca.pem"
struct config {
const char *uri;
const char *wrapper_server_addr;
const char *wrapper_client_addr;
const char *target_addr;
};
static struct config s_wrappers[] = {
{
"ws1:80",
"tcp://127.0.0.1:7001",
"tcp://127.0.0.1:7001",
"tcp://127.0.0.1:9001"
},
{
"ws1:443",
"ssl://127.0.0.1:7002:" S1_PEM,
"tcp://127.0.0.1:7002",
"tcp://127.0.0.1:9001"
},
{
"ws2:80",
"tcp://127.0.0.1:7003",
"tcp://127.0.0.1:7003",
"ssl://127.0.0.1:9002:" C2_PEM ":" CA2_PEM
},
{
"ws2:443",
"ssl://127.0.0.1:7004:" S2_PEM,
"tcp://127.0.0.1:7004",
"ssl://127.0.0.1:9002:" C2_PEM ":" CA2_PEM
},
};
static int s_received_signal = 0;
static void signal_handler(int sig_num) {
signal(sig_num, signal_handler);
s_received_signal = sig_num;
}
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
int i;
switch (ev) {
case MG_AUTH:
return MG_TRUE;
case MG_REQUEST:
printf("==> [%s] [%s]\n", conn->request_method, conn->uri);
if (strcmp(conn->request_method, "CONNECT") == 0) {
// Iterate over configured wrappers, see if we can use one of them
for (i = 0; i < (int) ARRAY_SIZE(s_wrappers); i++) {
if (strcmp(conn->uri, s_wrappers[i].uri) == 0) {
mg_forward(conn, s_wrappers[i].wrapper_client_addr);
return MG_MORE;
}
}
// No suitable wrappers found. Disallow that CONNECT request.
mg_send_status(conn, 405);
return MG_TRUE;
}
// Not a CONNECT request, serve HTML file.
mg_send_file(conn, "ws_ssl.html", NULL);
return MG_MORE;
default:
return MG_FALSE;
}
}
static int ws_handler(struct mg_connection *conn, enum mg_event ev) {
switch (ev) {
case MG_AUTH:
return MG_TRUE;
case MG_REQUEST:
if (conn->is_websocket) {
// Simple websocket echo server
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT,
conn->content, conn->content_len);
} else {
mg_printf_data(conn, "%s", "websocket connection expected");
}
return MG_TRUE;
default:
return MG_FALSE;
}
}
static void *serve_thread_func(void *param) {
struct mg_server *server = (struct mg_server *) param;
printf("Listening on port %s\n", mg_get_option(server, "listening_port"));
while (s_received_signal == 0) {
mg_poll_server(server, 1000);
}
mg_destroy_server(&server);
return NULL;
}
static void *wrapper_thread_func(void *param) {
struct config *c = (struct config *) param;
const char *err_msg;
void *wrapper;
wrapper = ssl_wrapper_init(c->wrapper_server_addr, c->target_addr, &err_msg);
if (wrapper == NULL) {
fprintf(stderr, "Error: %s\n", err_msg);
exit(EXIT_FAILURE);
}
//((struct ns_mgr *) wrapper)->hexdump_file = "/dev/stderr";
ssl_wrapper_serve(wrapper, &s_received_signal);
return NULL;
}
int main(void) {
struct mg_server *proxy_server = mg_create_server(NULL, ev_handler);
struct mg_server *ws1_server = mg_create_server(NULL, ws_handler);
struct mg_server *ws2_server = mg_create_server(NULL, ws_handler);
size_t i;
((struct ns_mgr *) proxy_server)->hexdump_file = "/dev/stderr";
// Configure proxy server to listen on port 2014
mg_set_option(proxy_server, "listening_port", "2014");
//mg_set_option(proxy_server, "enable_proxy", "yes");
// Configure two websocket echo servers:
// ws1 is WS, listening on 9001
// ws2 is WSS, listening on 9002
// Note that HTML page thinks that ws1 is WSS, and ws2 is WS,
// where in reality it is vice versa and proxy server makes the decision.
mg_set_option(ws1_server, "listening_port", "tcp://127.0.0.1:9001");
mg_set_option(ws2_server, "listening_port",
"ssl://127.0.0.1:9002:" S2_PEM ":" CA2_PEM);
// Setup signal handlers
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
// Start SSL wrappers, each in it's own thread
for (i = 0; i < ARRAY_SIZE(s_wrappers); i++) {
ns_start_thread(wrapper_thread_func, &s_wrappers[i]);
}
// Start websocket servers in separate threads
mg_start_thread(serve_thread_func, ws1_server);
mg_start_thread(serve_thread_func, ws2_server);
// Finally, start proxy server in this thread: this call blocks
serve_thread_func(proxy_server);
printf("Existing on signal %d\n", s_received_signal);
return EXIT_SUCCESS;
}
+50
View File
@@ -0,0 +1,50 @@
<html>
<head>
<title>Websocket Proxy SSL Test</title>
<meta charset="utf-8">
<script>
window.onload = function() {
var protocols = ['ws://', 'wss://'];
var websocketServers = ['ws1', 'ws2'];
//protocols = ['wss://'];
//websocketServers = ['ws1']
var createWebsocketConnection = function(proto, server) {
var conn = new WebSocket(proto + server);
var div = document.createElement('div');
var h2 = document.createElement('h2');
h2.innerHTML = 'Connection to ' + proto + server;
document.body.appendChild(h2);
document.body.appendChild(div);
conn.onmessage = function(ev) {
var el = document.createElement('div');
el.innerHTML = 'websocket message: ' + ev.data;
div.appendChild(el);
// Keep only last 5 messages in the list
while (div.childNodes.length > 5) div.removeChild(div.firstChild);
};
// Send some string to the websocket connection periodically.
// websocket server much echo it back.
window.setInterval(function() { conn.send(Math.random()); }, 1000);
};
for (var i = 0; i < protocols.length; i++) {
for (var j = 0; j < websocketServers.length; j++) {
createWebsocketConnection(protocols[i], websocketServers[j]);
}
}
};
</script>
<style>
body > div {
border: 1px solid #ccc; background: #f0f0f0; padding: 0 1em;
margin: 0 2em; min-height: 4em; max-width: 40em;
}
</style>
</head>
<body>
</body>
</html>
+8
View File
@@ -0,0 +1,8 @@
LOCAL_PATH := $(call my-dir)/..
include $(CLEAR_VARS)
LOCAL_CFLAGS := -std=c99 -O2 -W -Wall -pthread -pipe $(COPT)
LOCAL_MODULE := mongoose
LOCAL_SRC_FILES := examples/web_server/web_server.c mongoose.c
include $(BUILD_EXECUTABLE)
+2517 -1441
View File
File diff suppressed because it is too large. Load diff
+151 -123
View File
@@ -1,123 +1,151 @@
// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
// Copyright (c) 2013-2014 Cesanta Software Limited
// All rights reserved
//
// This library is dual-licensed: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation. For the terms of this
// license, see <http://www.gnu.org/licenses/>.
//
// You are free to use this library under the terms of the GNU General
// Public License, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// Alternatively, you can license this library under a commercial
// license, as set out in <http://cesanta.com/>.
//
// NOTE: Detailed API documentation is at http://cesanta.com/#docs
#ifndef MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_VERSION "5.2"
#include <stdio.h> // required for FILE
#include <stddef.h> // required for size_t
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// This structure contains information about HTTP request.
struct mg_connection {
const char *request_method; // "GET", "POST", etc
const char *uri; // URL-decoded URI
const char *http_version; // E.g. "1.0", "1.1"
const char *query_string; // URL part after '?', not including '?', or NULL
char remote_ip[48]; // Max IPv6 string length is 45 characters
const char *local_ip; // Local IP address
unsigned short remote_port; // Client's port
unsigned short local_port; // Local port number
int num_headers; // Number of HTTP headers
struct mg_header {
const char *name; // HTTP header name
const char *value; // HTTP header value
} http_headers[30];
char *content; // POST (or websocket message) data, or NULL
size_t content_len; // content length
int is_websocket; // Connection is a websocket connection
int status_code; // HTTP status code for HTTP error handler
int wsbits; // First byte of the websocket frame
void *server_param; // Parameter passed to mg_add_uri_handler()
void *connection_param; // Placeholder for connection-specific data
};
struct mg_server; // Opaque structure describing server instance
typedef int (*mg_handler_t)(struct mg_connection *);
// Server management functions
struct mg_server *mg_create_server(void *server_param);
void mg_destroy_server(struct mg_server **);
const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
unsigned int mg_poll_server(struct mg_server *, int milliseconds);
void mg_set_request_handler(struct mg_server *, mg_handler_t);
void mg_set_http_error_handler(struct mg_server *, mg_handler_t);
void mg_set_auth_handler(struct mg_server *, mg_handler_t);
const char **mg_get_valid_option_names(void);
const char *mg_get_option(const struct mg_server *server, const char *name);
void mg_set_listening_socket(struct mg_server *, int sock);
int mg_get_listening_socket(struct mg_server *);
void mg_iterate_over_connections(struct mg_server *, mg_handler_t, void *);
// Connection management functions
void mg_send_status(struct mg_connection *, int status_code);
void mg_send_header(struct mg_connection *, const char *name, const char *val);
void mg_send_data(struct mg_connection *, const void *data, int data_len);
void mg_printf_data(struct mg_connection *, const char *format, ...);
int mg_websocket_write(struct mg_connection *, int opcode,
const char *data, size_t data_len);
// Deprecated in favor of mg_send_* interface
int mg_write(struct mg_connection *, const void *buf, int len);
int mg_printf(struct mg_connection *conn, const char *fmt, ...);
const char *mg_get_header(const struct mg_connection *, const char *name);
const char *mg_get_mime_type(const char *name, const char *default_mime_type);
int mg_get_var(const struct mg_connection *conn, const char *var_name,
char *buf, size_t buf_len);
int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
int mg_parse_multipart(const char *buf, int buf_len,
char *var_name, int var_name_len,
char *file_name, int file_name_len,
const char **data, int *data_len);
// Utility functions
void *mg_start_thread(void *(*func)(void *), void *param);
char *mg_md5(char buf[33], ...);
int mg_authorize_digest(struct mg_connection *c, FILE *fp);
// Callback function return codes
enum { MG_REQUEST_NOT_PROCESSED, MG_REQUEST_PROCESSED, MG_REQUEST_CALL_AGAIN };
enum { MG_AUTH_FAIL, MG_AUTH_OK };
enum { MG_ERROR_NOT_PROCESSED, MG_ERROR_PROCESSED };
enum { MG_CLIENT_CONTINUE, MG_CLIENT_CLOSE };
// HTTP client events
enum {
MG_CONNECT_SUCCESS, MG_CONNECT_FAILURE,
MG_DOWNLOAD_SUCCESS, MG_DOWNLOAD_FAILURE
};
int mg_connect(struct mg_server *, const char *host, int port, int use_ssl,
mg_handler_t handler, void *param);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // MONGOOSE_HEADER_INCLUDED
// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
// Copyright (c) 2013-2014 Cesanta Software Limited
// All rights reserved
//
// This software is dual-licensed: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation. For the terms of this
// license, see <http://www.gnu.org/licenses/>.
//
// You are free to use this software under the terms of the GNU General
// Public License, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// Alternatively, you can license this software under a commercial
// license, as set out in <http://cesanta.com/>.
#ifndef MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_VERSION "5.6"
#include <stdio.h> // required for FILE
#include <stddef.h> // required for size_t
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// This structure contains information about HTTP request.
struct mg_connection {
const char *request_method; // "GET", "POST", etc
const char *uri; // URL-decoded URI
const char *http_version; // E.g. "1.0", "1.1"
const char *query_string; // URL part after '?', not including '?', or NULL
char remote_ip[48]; // Max IPv6 string length is 45 characters
char local_ip[48]; // Local IP address
unsigned short remote_port; // Client's port
unsigned short local_port; // Local port number
int num_headers; // Number of HTTP headers
struct mg_header {
const char *name; // HTTP header name
const char *value; // HTTP header value
} http_headers[30];
char *content; // POST (or websocket message) data, or NULL
size_t content_len; // Data length
int is_websocket; // Connection is a websocket connection
int status_code; // HTTP status code for HTTP error handler
int wsbits; // First byte of the websocket frame
void *server_param; // Parameter passed to mg_create_server()
void *connection_param; // Placeholder for connection-specific data
void *callback_param;
};
struct mg_server; // Opaque structure describing server instance
enum mg_result { MG_FALSE, MG_TRUE, MG_MORE };
enum mg_event {
MG_POLL = 100, // Callback return value is ignored
MG_CONNECT, // If callback returns MG_FALSE, connect fails
MG_AUTH, // If callback returns MG_FALSE, authentication fails
MG_REQUEST, // If callback returns MG_FALSE, Mongoose continues with req
MG_REPLY, // If callback returns MG_FALSE, Mongoose closes connection
MG_RECV, // Mongoose has received POST data chunk.
// Callback should return a number of bytes to discard from
// the receive buffer, or -1 to close the connection.
MG_CLOSE, // Connection is closed, callback return value is ignored
MG_WS_HANDSHAKE, // New websocket connection, handshake request
MG_WS_CONNECT, // New websocket connection established
MG_HTTP_ERROR // If callback returns MG_FALSE, Mongoose continues with err
};
typedef int (*mg_handler_t)(struct mg_connection *, enum mg_event);
// Websocket opcodes, from http://tools.ietf.org/html/rfc6455
enum {
WEBSOCKET_OPCODE_CONTINUATION = 0x0,
WEBSOCKET_OPCODE_TEXT = 0x1,
WEBSOCKET_OPCODE_BINARY = 0x2,
WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
WEBSOCKET_OPCODE_PING = 0x9,
WEBSOCKET_OPCODE_PONG = 0xa
};
// Server management functions
struct mg_server *mg_create_server(void *server_param, mg_handler_t handler);
void mg_destroy_server(struct mg_server **);
const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
int mg_poll_server(struct mg_server *, int milliseconds);
const char **mg_get_valid_option_names(void);
const char *mg_get_option(const struct mg_server *server, const char *name);
void mg_copy_listeners(struct mg_server *from, struct mg_server *to);
struct mg_connection *mg_next(struct mg_server *, struct mg_connection *);
void mg_wakeup_server(struct mg_server *);
void mg_wakeup_server_ex(struct mg_server *, mg_handler_t, const char *, ...);
struct mg_connection *mg_connect(struct mg_server *, const char *);
// Connection management functions
void mg_send_status(struct mg_connection *, int status_code);
void mg_send_header(struct mg_connection *, const char *name, const char *val);
size_t mg_send_data(struct mg_connection *, const void *data, int data_len);
size_t mg_printf_data(struct mg_connection *, const char *format, ...);
size_t mg_write(struct mg_connection *, const void *buf, int len);
size_t mg_printf(struct mg_connection *conn, const char *fmt, ...);
size_t mg_websocket_write(struct mg_connection *, int opcode,
const char *data, size_t data_len);
size_t mg_websocket_printf(struct mg_connection* conn, int opcode,
const char *fmt, ...);
void mg_send_file(struct mg_connection *, const char *path, const char *);
void mg_send_file_data(struct mg_connection *, int fd);
const char *mg_get_header(const struct mg_connection *, const char *name);
const char *mg_get_mime_type(const char *name, const char *default_mime_type);
int mg_get_var(const struct mg_connection *conn, const char *var_name,
char *buf, size_t buf_len);
int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
int mg_parse_multipart(const char *buf, int buf_len,
char *var_name, int var_name_len,
char *file_name, int file_name_len,
const char **data, int *data_len);
// Utility functions
void *mg_start_thread(void *(*func)(void *), void *param);
char *mg_md5(char buf[33], ...);
int mg_authorize_digest(struct mg_connection *c, FILE *fp);
int mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len);
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int);
int mg_terminate_ssl(struct mg_connection *c, const char *cert);
int mg_forward(struct mg_connection *c, const char *addr);
void *mg_mmap(FILE *fp, size_t size);
void mg_munmap(void *p, size_t size);
// Templates support
struct mg_expansion {
const char *keyword;
void (*handler)(struct mg_connection *);
};
void mg_template(struct mg_connection *, const char *text,
struct mg_expansion *expansions);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // MONGOOSE_HEADER_INCLUDED
File renamed without changes.
+21
View File
@@ -0,0 +1,21 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = unit_test
PROF_FLAGS = -fprofile-arcs -ftest-coverage -g -O0 -DGUI
CFLAGS = -W -Wall -pthread -I.. $(PROF_FLAGS) $(CFLAGS_EXTRA)
SOURCES = $(PROG).c
all: $(PROG)
./$(PROG)
gcov -b $(PROG).c | egrep '^(File|Lines)'
$(PROG): $(SOURCES) Makefile ../mongoose.c clean
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS) -ldl -lssl
win:
wine cl $(SOURCES) /MD /nologo /DNDEBUG /O1 /Fe$(PROG).exe
wine $(PROG).exe
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib *.gc*
+125 -238
View File
@@ -3,13 +3,13 @@
// cl unit_test.c /MD
#ifndef _WIN32
#define MONGOOSE_USE_IPV6
#define MONGOOSE_USE_SSL
#define NS_ENABLE_IPV6
#define NS_ENABLE_SSL
#endif
#define MONGOOSE_USE_POST_SIZE_LIMIT 999
#define MONGOOSE_POST_SIZE_LIMIT 999
// USE_* definitions must be made before #include "mongoose.c" !
#include "mongoose.c"
#include "../mongoose.c"
#define FAIL(str, line) do { \
printf("Fail on line %d: [%s]\n", line, str); \
@@ -166,34 +166,34 @@ static const char *test_should_keep_alive(void) {
}
static const char *test_match_prefix(void) {
ASSERT(match_prefix("/api", 4, "/api") == 4);
ASSERT(match_prefix("/a/", 3, "/a/b/c") == 3);
ASSERT(match_prefix("/a/", 3, "/ab/c") == -1);
ASSERT(match_prefix("/*/", 3, "/ab/c") == 4);
ASSERT(match_prefix("**", 2, "/a/b/c") == 6);
ASSERT(match_prefix("/*", 2, "/a/b/c") == 2);
ASSERT(match_prefix("*/*", 3, "/a/b/c") == 2);
ASSERT(match_prefix("**/", 3, "/a/b/c") == 5);
ASSERT(match_prefix("**.foo|**.bar", 13, "a.bar") == 5);
ASSERT(match_prefix("a|b|cd", 6, "cdef") == 2);
ASSERT(match_prefix("a|b|c?", 6, "cdef") == 2);
ASSERT(match_prefix("a|?|cd", 6, "cdef") == 1);
ASSERT(match_prefix("/a/**.cgi", 9, "/foo/bar/x.cgi") == -1);
ASSERT(match_prefix("/a/**.cgi", 9, "/a/bar/x.cgi") == 12);
ASSERT(match_prefix("**/", 3, "/a/b/c") == 5);
ASSERT(match_prefix("**/$", 4, "/a/b/c") == -1);
ASSERT(match_prefix("**/$", 4, "/a/b/") == 5);
ASSERT(match_prefix("$", 1, "") == 0);
ASSERT(match_prefix("$", 1, "x") == -1);
ASSERT(match_prefix("*$", 2, "x") == 1);
ASSERT(match_prefix("/$", 2, "/") == 1);
ASSERT(match_prefix("**/$", 4, "/a/b/c") == -1);
ASSERT(match_prefix("**/$", 4, "/a/b/") == 5);
ASSERT(match_prefix("*", 1, "/hello/") == 0);
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b/") == -1);
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6);
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/B.A") == 6);
ASSERT(match_prefix("**o$", 4, "HELLO") == 5);
ASSERT(mg_match_prefix("/api", 4, "/api") == 4);
ASSERT(mg_match_prefix("/a/", 3, "/a/b/c") == 3);
ASSERT(mg_match_prefix("/a/", 3, "/ab/c") == -1);
ASSERT(mg_match_prefix("/*/", 3, "/ab/c") == 4);
ASSERT(mg_match_prefix("**", 2, "/a/b/c") == 6);
ASSERT(mg_match_prefix("/*", 2, "/a/b/c") == 2);
ASSERT(mg_match_prefix("*/*", 3, "/a/b/c") == 2);
ASSERT(mg_match_prefix("**/", 3, "/a/b/c") == 5);
ASSERT(mg_match_prefix("**.foo|**.bar", 13, "a.bar") == 5);
ASSERT(mg_match_prefix("a|b|cd", 6, "cdef") == 2);
ASSERT(mg_match_prefix("a|b|c?", 6, "cdef") == 2);
ASSERT(mg_match_prefix("a|?|cd", 6, "cdef") == 1);
ASSERT(mg_match_prefix("/a/**.cgi", 9, "/foo/bar/x.cgi") == -1);
ASSERT(mg_match_prefix("/a/**.cgi", 9, "/a/bar/x.cgi") == 12);
ASSERT(mg_match_prefix("**/", 3, "/a/b/c") == 5);
ASSERT(mg_match_prefix("**/$", 4, "/a/b/c") == -1);
ASSERT(mg_match_prefix("**/$", 4, "/a/b/") == 5);
ASSERT(mg_match_prefix("$", 1, "") == 0);
ASSERT(mg_match_prefix("$", 1, "x") == -1);
ASSERT(mg_match_prefix("*$", 2, "x") == 1);
ASSERT(mg_match_prefix("/$", 2, "/") == 1);
ASSERT(mg_match_prefix("**/$", 4, "/a/b/c") == -1);
ASSERT(mg_match_prefix("**/$", 4, "/a/b/") == 5);
ASSERT(mg_match_prefix("*", 1, "/hello/") == 0);
ASSERT(mg_match_prefix("**.a$|**.b$", 11, "/a/b.b/") == -1);
ASSERT(mg_match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6);
ASSERT(mg_match_prefix("**.a$|**.b$", 11, "/a/B.A") == 6);
ASSERT(mg_match_prefix("**o$", 4, "HELLO") == 5);
return NULL;
}
@@ -271,6 +271,17 @@ static const char *test_url_decode(void) {
return NULL;
}
static const char *test_url_encode(void) {
char buf[100];
ASSERT(mg_url_encode("", 0, buf, sizeof(buf)) == 0);
ASSERT(buf[0] == '\0');
ASSERT(mg_url_encode("foo", 3, buf, sizeof(buf)) == 3);
ASSERT(strcmp(buf, "foo") == 0);
ASSERT(mg_url_encode("f o", 3, buf, sizeof(buf)) == 5);
ASSERT(strcmp(buf, "f%20o") == 0);
return NULL;
}
static const char *test_to64(void) {
ASSERT(to64("0") == 0);
ASSERT(to64("") == 0);
@@ -280,32 +291,6 @@ static const char *test_to64(void) {
return NULL;
}
static const char *test_parse_port_string(void) {
static const char *valid[] = {
"1", "1.2.3.4:1",
#if defined(USE_IPV6)
"[::1]:123", "[3ffe:2a00:100:7031::1]:900",
#endif
NULL
};
static const char *invalid[] = {
"99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p", NULL
};
union socket_address sa;
int i;
for (i = 0; valid[i] != NULL; i++) {
ASSERT(parse_port_string(valid[i], &sa) != 0);
}
for (i = 0; invalid[i] != NULL; i++) {
ASSERT(parse_port_string(invalid[i], &sa) == 0);
}
ASSERT(parse_port_string("0", &sa) != 0);
return NULL;
}
static const char *test_base64_encode(void) {
const char *in[] = {"a", "ab", "abc", "abcd", NULL};
const char *out[] = {"YQ==", "YWI=", "YWJj", "YWJjZA=="};
@@ -321,9 +306,9 @@ static const char *test_base64_encode(void) {
}
static const char *test_mg_parse_header(void) {
const char *str = "xx=1 kl yy, ert=234 kl=123, "
const char *str = "xx=1 kl yy, ert=234 kl=123, uri=\"/?name=x,y\", "
"ii=\"12\\\"34\" zz='aa bb',tt=2,gf=\"xx d=1234";
char buf[10];
char buf[20];
ASSERT(mg_parse_header(str, "yy", buf, sizeof(buf)) == 0);
ASSERT(mg_parse_header(str, "ert", buf, sizeof(buf)) == 3);
ASSERT(strcmp(buf, "234") == 0);
@@ -346,6 +331,7 @@ static const char *test_mg_parse_header(void) {
ASSERT(strcmp(buf, "12\"34") == 0);
ASSERT(mg_parse_header(str, "tt", buf, sizeof(buf)) == 1);
ASSERT(strcmp(buf, "2") == 0);
ASSERT(mg_parse_header(str, "uri", buf, sizeof(buf)) == 10);
return NULL;
}
@@ -365,60 +351,60 @@ static const char *test_next_option(void) {
return NULL;
}
static int cb1(struct mg_connection *conn) {
int result = MG_REQUEST_NOT_PROCESSED;
if (!strcmp(conn->uri, "/cb1")) {
mg_printf(conn, "%s %s %s",
conn->server_param == NULL ? "?" : (char *) conn->server_param,
conn->connection_param == NULL ? "?" : "!", conn->remote_ip);
result = MG_REQUEST_PROCESSED;
static int evh1(struct mg_connection *conn, enum mg_event ev) {
char *buf = (char *) conn->connection_param;
int result = MG_FALSE;
switch (ev) {
case MG_CONNECT:
mg_printf(conn, "GET %s HTTP/1.0\r\n\r\n",
buf[0] == '1' ? "/cb1" : "/non_exist");
result = MG_TRUE;
break;
case MG_HTTP_ERROR:
mg_printf(conn, "HTTP/1.0 404 NF\r\n\r\nERR: %d", conn->status_code);
result = MG_TRUE;
break;
case MG_REQUEST:
if (!strcmp(conn->uri, "/cb1")) {
mg_printf(conn, "HTTP/1.0 200 OK\r\n\r\n%s %s %s",
(char *) conn->server_param,
buf == NULL ? "?" : "!", conn->remote_ip);
result = MG_TRUE;
}
break;
case MG_REPLY:
if (buf != NULL) {
sprintf(buf + 1, "%.*s", (int) conn->content_len, conn->content);
}
break;
case MG_AUTH:
result = MG_TRUE;
break;
default:
break;
}
return result;
}
static int error_handler(struct mg_connection *conn) {
mg_printf(conn, "HTTP/1.0 404 NF\r\n\r\nERR: %d", conn->status_code);
return MG_ERROR_PROCESSED;
}
static int ts1(struct mg_connection *conn) {
if (conn->status_code == MG_CONNECT_SUCCESS) {
mg_printf(conn, "%s", "GET /cb1 HTTP/1.0\r\n\r\n");
return MG_CLIENT_CONTINUE;
} else if (conn->status_code == MG_DOWNLOAD_SUCCESS) {
sprintf((char *) conn->connection_param, "%.*s",
(int) conn->content_len, conn->content);
}
return MG_CLIENT_CLOSE;
}
static int ts2(struct mg_connection *conn) {
if (conn->status_code == MG_CONNECT_SUCCESS) {
mg_printf(conn, "%s", "GET /non_exist HTTP/1.0\r\n\r\n");
return MG_CLIENT_CONTINUE;
} else if (conn->status_code == MG_DOWNLOAD_SUCCESS) {
sprintf((char *) conn->connection_param, "%s %.*s",
conn->uri, (int) conn->content_len, conn->content);
}
return MG_CLIENT_CLOSE;
}
static const char *test_server(void) {
char buf1[100] = "", buf2[100] = "";
struct mg_server *server = mg_create_server((void *) "foo");
char buf1[100] = "1", buf2[100] = "2";
struct mg_server *server = mg_create_server((void *) "foo", evh1);
struct mg_connection *conn;
ASSERT(server != NULL);
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
mg_set_request_handler(server, cb1);
mg_set_http_error_handler(server, error_handler);
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, ts1, buf1) == 1);
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, ts2, buf2) == 1);
ASSERT((conn = mg_connect(server, "127.0.0.1:" HTTP_PORT)) != NULL);
conn->connection_param = buf1;
ASSERT((conn = mg_connect(server, "127.0.0.1:" HTTP_PORT)) != NULL);
conn->connection_param = buf2;
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
ASSERT(strcmp(buf1, "foo ? 127.0.0.1") == 0);
ASSERT(strcmp(buf2, "404 ERR: 404") == 0);
ASSERT(strcmp(buf1, "1foo ? 127.0.0.1") == 0);
ASSERT(strcmp(buf2, "2ERR: 404") == 0);
ASSERT(strcmp(static_config_options[URL_REWRITES * 2], "url_rewrites") == 0);
mg_destroy_server(&server);
@@ -426,90 +412,6 @@ static const char *test_server(void) {
return NULL;
}
// This http client sends two requests using one connection
static int cb2(struct mg_connection *conn) {
const char *file1 = "mongoose.h", *file2 = "mongoose.c", *file_name = file2;
char *buf = (char *) conn->connection_param, *file_data;
int file_len, ret_val = 1;
switch (conn->status_code) {
case MG_CONNECT_SUCCESS:
mg_printf(conn, "GET /%s HTTP/1.1\r\n\r\n", file1);
strcat(buf, "a");
ret_val = 0;
break;
case MG_CONNECT_FAILURE: strcat(buf, "b"); break;
case MG_DOWNLOAD_FAILURE: strcat(buf, "c"); break;
case MG_DOWNLOAD_SUCCESS:
if (!strcmp(buf, "a")) {
// Send second request
mg_printf(conn, "GET /%s HTTP/1.1\r\n\r\n", file2);
file_name = file1;
}
if ((file_data = read_file(file_name, &file_len)) != NULL) {
if ((size_t) file_len == conn->content_len &&
memcmp(file_data, conn->content, file_len) == 0) {
strcat(buf, "d");
} else {
strcat(buf, "f");
}
free(file_data);
}
ret_val = !strcmp(buf, "ad") ? 0 : 1;
break;
default: strcat(buf, "e"); break;
}
return ret_val;
}
static int cb4h(struct mg_connection *conn) {
int result = MG_REQUEST_NOT_PROCESSED;
if (!strcmp(conn->uri, "/x")) {
mg_send_data(conn, ":-)", 3);
result = MG_REQUEST_PROCESSED;
}
return result;
}
static int cb4(struct mg_connection *conn) {
if (conn->status_code == MG_CONNECT_SUCCESS) {
mg_printf(conn, "%s", "POST /x HTTP/1.0\r\nContent-Length: 999999\r\n\r\n");
return MG_CLIENT_CONTINUE;
} else {
sprintf((char *) conn->connection_param, "%.*s",
(int) conn->content_len, conn->content);
}
return MG_CLIENT_CLOSE;
}
static int cb3(struct mg_connection *conn) {
sprintf((char *) conn->connection_param, "%d", conn->status_code);
return MG_CLIENT_CLOSE;
}
static const char *test_mg_connect(void) {
char buf2[40] = "", buf3[40] = "", buf4[40] = "";
struct mg_server *server = mg_create_server(NULL);
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
mg_set_request_handler(server, cb4h);
ASSERT(mg_connect(server, "", 0, 0, NULL, NULL) == 0);
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, cb2, buf2) == 1);
ASSERT(mg_connect(server, "127.0.0.1", 29, 0, cb3, buf3) == 1);
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, cb4, buf4) == 1);
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
ASSERT(strcmp(buf2, "add") == 0);
ASSERT(strcmp(buf3, "1") == 0);
ASSERT(strcmp(buf4, "500 Server Error\nPOST size > 999") == 0);
mg_destroy_server(&server);
return NULL;
}
#define DISP "Content-Disposition: form/data; "
#define CRLF "\r\n"
#define BOUNDARY "--xyz"
@@ -545,49 +447,36 @@ static const char *test_parse_multipart(void) {
return NULL;
}
static int us1(struct mg_connection *conn) {
static const char *file_name = "mongoose.h";
char *cp = (char *) conn->connection_param;
int file_size;
static int evh2(struct mg_connection *conn, enum mg_event ev) {
char *file_data, *cp = (char *) conn->connection_param;
int file_size, result = MG_FALSE;
if (conn->status_code == MG_CONNECT_SUCCESS) {
mg_printf(conn, "GET /%s HTTP/1.0\r\n\r\n", cp);
return 0;
} else if (conn->status_code == MG_DOWNLOAD_SUCCESS) {
char *file_data = read_file(file_name, &file_size);
sprintf(cp, "%d %s", (size_t) file_size == conn->content_len &&
memcmp(file_data, conn->content, file_size) == 0 ? 1 : 0,
conn->query_string == NULL ? "?" : conn->query_string);
free(file_data);
} else if (conn->status_code == MG_CONNECT_FAILURE) {
sprintf(cp, "%s", "cf");
} else {
sprintf(cp, "%s", "df");
switch (ev) {
case MG_AUTH:
result = MG_TRUE;
break;
case MG_CONNECT:
mg_printf(conn, "GET /%s HTTP/1.0\r\n\r\n", cp);
result = MG_TRUE;
break;
case MG_REQUEST:
break;
case MG_REPLY:
file_data = read_file("unit_test.c", &file_size);
sprintf(cp, "%d %s", (size_t) file_size == conn->content_len &&
memcmp(file_data, conn->content, file_size) == 0 ? 1 : 0,
conn->query_string == NULL ? "?" : conn->query_string);
free(file_data);
break;
default:
break;
}
return 1;
return result;
}
#ifdef MONGOOSE_USE_SSL
static const char *test_ssl(void) {
static const char *ssl_cert = "examples/ssl_cert.pem";
char buf1[100] = "mongoose.h";
struct mg_server *server = mg_create_server(NULL);
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
ASSERT(mg_set_option(server, "ssl_certificate", ssl_cert) == NULL);
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 1, us1, buf1) == 1);
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
ASSERT(strcmp(buf1, "1 ?") == 0);
mg_destroy_server(&server);
return NULL;
}
#endif
static const char *test_mg_set_option(void) {
struct mg_server *server = mg_create_server(NULL);
struct mg_server *server = mg_create_server(NULL, NULL);
ASSERT(mg_set_option(server, "listening_port", "0") == NULL);
ASSERT(mg_get_option(server, "listening_port")[0] != '\0');
mg_destroy_server(&server);
@@ -595,19 +484,21 @@ static const char *test_mg_set_option(void) {
}
static const char *test_rewrites(void) {
char buf1[100] = "xx";
struct mg_server *server = mg_create_server(NULL);
char buf1[100] = "xx", addr[50];
struct mg_server *server = mg_create_server(NULL, evh2);
struct mg_connection *conn;
const char *port;
ASSERT(mg_set_option(server, "listening_port", "0") == NULL);
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
ASSERT(mg_set_option(server, "url_rewrites", "/xx=./mongoose.h") == NULL);
ASSERT(mg_connect(server, "127.0.0.1",
atoi(mg_get_option(server, "listening_port")),
0, us1, buf1) == 1);
ASSERT(mg_set_option(server, "url_rewrites", "/xx=unit_test.c") == NULL);
ASSERT((port = mg_get_option(server, "listening_port")) != NULL);
snprintf(addr, sizeof(addr), "127.0.0.1:%s", port);
ASSERT((conn = mg_connect(server, addr)) != NULL);
conn->connection_param = buf1;
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
//printf("[%s]\n", buf1);
ASSERT(strcmp(buf1, "1 ?") == 0);
mg_destroy_server(&server);
return NULL;
@@ -618,21 +509,17 @@ static const char *run_all_tests(void) {
RUN_TEST(test_match_prefix);
RUN_TEST(test_remove_double_dots);
RUN_TEST(test_parse_http_message);
RUN_TEST(test_parse_port_string);
RUN_TEST(test_to64);
RUN_TEST(test_url_decode);
RUN_TEST(test_url_encode);
RUN_TEST(test_base64_encode);
RUN_TEST(test_mg_parse_header);
RUN_TEST(test_get_var);
RUN_TEST(test_next_option);
RUN_TEST(test_parse_multipart);
RUN_TEST(test_server);
RUN_TEST(test_mg_connect);
RUN_TEST(test_mg_set_option);
RUN_TEST(test_server);
RUN_TEST(test_rewrites);
#ifdef MONGOOSE_USE_SSL
RUN_TEST(test_ssl);
#endif
return NULL;
}