Compare commits

..
250 Commits
Author SHA1 Message Date
Deomid Ryabkov 81d977c720 Introduce mg_time() and use it internally
PUBLISHED_FROM=024a2d7849a800381d58460877eacccfc93b1cdd
2016-01-11 12:24:40 +03:00
Alexander Alashkin f477898c7c Mongoose 6.1 -> 6.2
PUBLISHED_FROM=24146cf7f4cf86cfd76260fb906d50889b9d6f7e
2016-01-11 12:24:40 +03:00
Marko Mikulicic 566193a1eb Implement freezing V7 object graph
PUBLISHED_FROM=1670ce871ed2c9391edb10ff413d3d454997d1fe
2016-01-11 12:24:40 +03:00
Deomid Ryabkov 5d6eeb5d1d Add a note about Content-Type for form submissions
PUBLISHED_FROM=6079258be24fa0255f5a58e4d364be224435394b
2016-01-11 12:24:39 +03:00
Dmitry Frank d9b9d71b2d v7_apply() and friends take res as last argument
PUBLISHED_FROM=4b064f5e4554c5a016481bf8104c0ba9dd244083
2016-01-11 12:24:38 +03:00
Dmitry Frank 643ee1df14 cfunctions API refactored to return enum v7_err
PUBLISHED_FROM=f96a4ab2016c0125c1e94b3c5c9ada2c365bd39d
2016-01-11 12:24:36 +03:00
Deomid Ryabkov 6c4d37ee68 Free previous ssl context in mg_set_ssl
PUBLISHED_FROM=9f595222bce1fe8d0917649e36ce3be40eee1775
2015-12-23 17:02:52 +01:00
Sergey Lyubka 97b79cd04a Add MacOS to mongoose README
PUBLISHED_FROM=679b640e3de8b0fbd65b38263093dd6049db4cd3
2015-12-23 17:02:52 +01:00
Sergey Lyubka 56c6229dd6 Update Mongoose readme
PUBLISHED_FROM=ac117f203ef11b9c3877a6dd1919e8409d04cdb1
2015-12-22 07:42:12 +00:00
Dmitry Frank 0fe2dd9b48 Added param names to mongoose headers
PUBLISHED_FROM=07bd8163c4e1df49ab3dc7a1640efa2adff34e33
2015-12-22 07:42:11 +00:00
Sergey Lyubka 910aee7f7b Re-enable mongosoe FS support on ESP
PUBLISHED_FROM=cfa1c79bb5429feddf38c68c25195b97deef9a62
2015-12-22 07:33:06 +00:00
Sergey Lyubka 97063937ff Add vanilla forum to forum.cesanta.com
PUBLISHED_FROM=bff863108d0fb80a6139ac34be04fb999e18e62e
2015-12-22 07:33:06 +00:00
Sergey Lyubka 7e68b097d1 Close https://github.com/cesanta/mongoose/pull/603
PUBLISHED_FROM=ab934b21ed67878f47ccd569d88030e53e914bf7
2015-12-21 08:37:20 +00:00
Marko Mikulicic 4d41dff4fd Fix includes
PUBLISHED_FROM=58d764bb224968d5368b21a8eb572602055cb26b
2015-12-21 08:37:19 +00:00
Deomid Ryabkov 05082cec88 Use sub-second time for timers in MG
PUBLISHED_FROM=2b5bd1589465a5f43bc888c689842332cf7cdf98
2015-12-20 17:49:13 +00:00
Marko Mikulicic f05733da69 Fix COAP example, reduce number of ctx switches
PUBLISHED_FROM=7010fff404826c3dfc783d2cbdcbd0936a8a2b85
2015-12-15 11:36:34 +01:00
Marko Mikulicic 3c53909e82 Foolproof Smartjs publishing
PUBLISHED_FROM=8ade849890746baa95fd8a5d92baf3dda656a719
2015-12-15 11:28:39 +01:00
Deomid Ryabkov 1e53ec47a9 Peeple TLS support for updates
PUBLISHED_FROM=1abdacaece6dc63b6148c605c4c2cc41c3cc4ac6
2015-12-15 11:28:38 +01:00
Sergey Lyubka 17802735d7 Spiffy config for cc3200
PUBLISHED_FROM=34b3678cb2510adfd5f26183ae7c16aadb97e6c9
2015-12-14 00:12:12 +01:00
Marko Mikulicic d2eb67a1ae Sanitize mongoose headers
PUBLISHED_FROM=d3790d175b8d714865c7eb5f12e82d31844a863f
2015-12-14 00:12:12 +01:00
Marko Mikulicic 3f254dd3fc Simplify include paths
PUBLISHED_FROM=4e38db207dc2634e410339c81c5a2a87e8b188b0
2015-12-14 00:12:12 +01:00
Deomid Ryabkov e139bde6ae A couple SSL-related fixes for Mongoose
PUBLISHED_FROM=03297850f7a620588de851ef86be25e17a04ad28
2015-12-11 13:02:13 +00:00
Sergey Lyubka 5f62951143 Runtime DAV auth disable. Implement MOVE
PUBLISHED_FROM=aec49928891972ecd6af9999fe3e1cb5ef00f24c
2015-12-11 13:02:13 +00:00
Sergey Lyubka 2207e87c8f Fix DAV for non-existent files. Improve logging
PUBLISHED_FROM=39a2810ad9bebcf1676b982f7523aa9c2339cc4f
2015-12-11 13:02:12 +00:00
Sergey Lyubka 5bd59c7b70 Shut up spiffs. Proper DAV report
PUBLISHED_FROM=125379f6c624b4e938b5d543cb8ed5fe906de98f
2015-12-11 13:02:12 +00:00
Alexander Alashkin 02f6867a2f Implement %lld in c_vsnprintf. Closes cesanta/dev#2120
PUBLISHED_FROM=1f24878425356f625b4f5b2b0fe40ab5f17501ab
2015-12-11 13:02:11 +00:00
Deomid Ryabkov b93fa3144a First cut of TLS support for LWIP event manager
PUBLISHED_FROM=98cf23ea090ffb4e83edd4d600bee57dfd7d94d4
2015-12-11 13:02:11 +00:00
Alexander Alashkin a68e0d9a3f Add WebDAV move stub
PUBLISHED_FROM=b1f712862b81dfe8da6547338f77ff3bfefa5051
2015-12-11 13:02:10 +00:00
Alexander Alashkin cfe80ba9b7 Add WebDAV support to sjs/esp8266
PUBLISHED_FROM=a68a2f205cb2f10809183d5160709981585538b7
2015-12-11 13:02:10 +00:00
Deomid Ryabkov 35abfd44dd Fix windows build
PUBLISHED_FROM=3b3788da8c2dcc57897044701c5252ea46db0249
2015-12-08 13:38:05 +00:00
Sergey Lyubka e98b5a691e Implement Http_request.setTimeout()
PUBLISHED_FROM=7a72e485aad2be859b9452dc1687a922778959d1
2015-12-08 13:38:05 +00:00
Sergey Lyubka ed24afd172 Connect timeouts for resolving connections
PUBLISHED_FROM=068b6f75550b10913d7f13106f9a7f7dce5babec
2015-12-08 12:35:29 +00:00
Deomid Ryabkov 714556de65 Added debug line to mg_mgr_handle_ctl_sock
PUBLISHED_FROM=a4c1d105cb4f18d982bd28190691b6ea65ee37fc
2015-12-08 12:35:29 +00:00
Deomid Ryabkov 547ec85b58 Properly disable socketpair-related stuff
PUBLISHED_FROM=f3a236470281af80848c8f78163980083190c081
2015-12-08 12:35:29 +00:00
Marko Mikulicic 845d751e87 Fix C String API confusion
PUBLISHED_FROM=af34acd81d877bfbd8441c14451d8c81f18c207d
2015-12-08 12:35:29 +00:00
Alexander Alashkin 518a7f8a71 Fix MG DAV
PUBLISHED_FROM=132db6ed9c9e1eb9ec19b4f9299448b901b92231
2015-12-08 12:35:29 +00:00
Sergey Lyubka 31323b01bc Introduce MG_EV_TIMER event
PUBLISHED_FROM=2e90ee87eda8bc742e427671bb9df1ce0cd8bc25
2015-12-08 12:35:28 +00:00
Deomid Ryabkov 3a138ecc83 Move more of SSL-specific code into net_if
PUBLISHED_FROM=47dc4712c0ea2012c0774ad260f0451371cfe8b7
2015-12-08 12:35:28 +00:00
Dmitry Frank 7294674a8b Remove custom strnlen for VC 16+
PUBLISHED_FROM=9c28b64267408929f5c565b87978816f3a93f06b
2015-12-08 12:35:27 +00:00
Sergey Lyubka 423b978395 Report read-only vars to UI
PUBLISHED_FROM=2ca4b52e777a54d9bb05a177e57a8b73bb962fdb
2015-12-08 12:35:27 +00:00
Sergey Lyubka ecf20ec7b8 Add error_log_file, use extended log format
PUBLISHED_FROM=d189b44857723871159826944bde9b87089bb0bd
2015-12-08 12:35:27 +00:00
Sergey Lyubka c948a509fb Export mg_url_decode() to public
PUBLISHED_FROM=442534f52992be612a98a549e5ab2191081a9d52
2015-12-03 22:05:52 +00:00
Sergey Lyubka f5ad19d76b Fix g++ build for mongoose example
PUBLISHED_FROM=c581f33e26819d05c9b8484c9dc10c2a3615a34a
2015-12-02 17:04:44 +00:00
Deomid Ryabkov d665d9e90e Always Be Closing
PUBLISHED_FROM=29ca7cdd9024790bd75892f9f67b3f83a6bd00d1
2015-12-01 18:03:19 +00:00
Sergey Lyubka a5e10450dd Mongoose 6.0 -> 6.1
PUBLISHED_FROM=22408f9340bbbbf186bd551dd30564efe0c253e3
2015-11-30 12:27:59 +00:00
Sergey Lyubka e9d79b130d Add documentation for the mongoose binary
PUBLISHED_FROM=71a01298c9bbff9a748ea33e0d148d549e5e6437
2015-11-30 12:27:59 +00:00
Deomid Ryabkov 7951dac9d5 Put getting local/remote address behinf net_if
PUBLISHED_FROM=f88056e79b632d9eee288dacf61c2f87ff3ceca5
2015-11-30 12:27:58 +00:00
Alexander Alashkin 6c77351c58 Verify settings before apply
PUBLISHED_FROM=ca7b6a9814b88b45d893d8e1386faded27305e9c
2015-11-30 12:27:58 +00:00
Sergey Lyubka 7c4cda622f Implement port based redirect
PUBLISHED_FROM=798a8bc38cb385027d3caae9b954002c1a10d39c
2015-11-30 12:27:57 +00:00
Deomid Ryabkov d8f8c1cb99 I fail at math
PUBLISHED_FROM=c45da0cf83e9573a9fa8234f4dee1b9e4e5b977a
2015-11-30 12:27:57 +00:00
Deomid Ryabkov d2b9ad570f Add a custom linker script for 4m flash
PUBLISHED_FROM=a92b86588923fb54075b1c3ed8ca8112164c36c5
2015-11-25 12:06:28 +00:00
Deomid Ryabkov 5b36f32996 Add ESP8266 RTOS example
PUBLISHED_FROM=892d01b09f5801d3d19e6db1ce22b7916d2f83d5
2015-11-24 00:42:27 +00:00
Deomid Ryabkov 3298030b8e Changes to make Mongoose compile on ESP RTOS SDK
PUBLISHED_FROM=863a47416ee5e30f20f6523374ebd510cc022d99
2015-11-24 00:42:27 +00:00
Sergey Lyubka 6cfc696439 Add helper function mg_send_head()
PUBLISHED_FROM=5c9d11d21d34fa72974a021f0342068d87159634
2015-11-24 00:42:26 +00:00
Sergey Lyubka a93b3a06dd Change binary download link
PUBLISHED_FROM=bc1a39dc8c11bab83c20da2c6e9477da0d17f993
2015-11-20 12:07:52 +00:00
Deomid Ryabkov 79e8d77ea0 Shave the debug yak
PUBLISHED_FROM=402d26b6a2797bfe9fb70e48cbde97f3960ef5d4
2015-11-20 12:07:51 +00:00
Sergey Lyubka d68fab9ee5 Enable threads on Win by default
PUBLISHED_FROM=cc4070d618f079ccd1e02e62bba87247e75c74aa
2015-11-20 12:07:51 +00:00
Sergey Lyubka bda470245e Support for multiple listeners
PUBLISHED_FROM=ad00f31ae8732be2d7fdc7f0968fd04edfd76bb6
2015-11-20 12:07:51 +00:00
Sergey Lyubka 0c95290f93 Add extra headers support (allows CORS)
PUBLISHED_FROM=2279e31315bba4d19e2ae568cba94ea5dea3ef6a
2015-11-18 07:44:23 +00:00
Deomid Ryabkov 8989739d32 Use mg_call to invoke user handler
PUBLISHED_FROM=cefd472946dfe77ec262c1980eab983a919b6220
2015-11-18 07:44:23 +00:00
Sergey Lyubka 1112981e6e Fix mg_if_not_modified() logic
PUBLISHED_FROM=3eb1455eacc66af83904f56397c0e24b1d4f13bf
2015-11-18 07:44:23 +00:00
Sergey Lyubka 0cf82744ce Unbreak macos tests
PUBLISHED_FROM=a9521579223cfbb68e2b7bcd221335d25dacbb87
2015-11-18 07:44:22 +00:00
Deomid Ryabkov 5cdf838325 Fix error handling in async resolver
PUBLISHED_FROM=1e8558ec08f8e72d70163d34e0920f80f74372d1
2015-11-18 07:44:22 +00:00
Sergey Lyubka 6418cad39f Disable HTTP keep-alive on embedded
PUBLISHED_FROM=63bb1ca0f307a4332006d6cd13a35f0be6359174
2015-11-18 07:44:22 +00:00
Sergey Lyubka 46529fd51e Remove unused MG_F_DONT_SEND
PUBLISHED_FROM=beaec58b808fb767b7e491d8b5d6b23a6d5e4e0c
2015-11-18 07:44:21 +00:00
Marko Mikulicic c09ba40a48 Cleanup make format target
PUBLISHED_FROM=5f856af633ca9a10161769caac130fc6452f137f
2015-11-18 07:44:20 +00:00
Sergey Lyubka 47d52896a1 s/uri/url, to conform to nodejs HTTP API
PUBLISHED_FROM=32ba0c859ad9df3cf7dc3af8e379e49543e77843
2015-11-18 07:44:19 +00:00
Deomid Ryabkov d60c99b01c Make MG debug go to stderr
PUBLISHED_FROM=11618b6b2a50c62dd612d241c6b13a9aab357909
2015-11-18 07:44:19 +00:00
Sergey Lyubka b8aeba06a2 Add unit test for mg_parse_date_string
PUBLISHED_FROM=aec7d5e6c6598fbc5141c40c9638cc96d288c8dc
2015-11-18 07:44:18 +00:00
Sergey Lyubka 8a0bc2d614 Mongoose binary - GUI version fix
PUBLISHED_FROM=94c2b4c5a40c3519df6caa7561c3935bdf505575
2015-11-18 07:44:18 +00:00
Deomid Ryabkov e34747d1ad Handle case of malloc failure in mbuf_resize
PUBLISHED_FROM=0cb98ac520b8aeb8bbeb2f23b8c65c62ab256021
2015-10-30 18:38:43 +01:00
Deomid Ryabkov 1a6bc7c514 Fix mbuf_resize(0)
PUBLISHED_FROM=7cab7543db990f7f67b53edca4f65ba45b219711
2015-10-30 18:38:42 +01:00
Deomid Ryabkov ffa0ddfdbf Make mg_call usable in protocol handlers
PUBLISHED_FROM=63ad74b31750a851db090d2f2995ecf5f275538b
2015-10-30 18:38:42 +01:00
Alexander Alashkin ca55bff1cf Fix mg compilation
PUBLISHED_FROM=ce7fdfebd3ebbf88688514be501b33ad1241743c
2015-10-30 18:38:42 +01:00
Deomid Ryabkov cfee5c4fdb Mongoose TCP listener for ESP LWIP
PUBLISHED_FROM=e88b17d21ee78a4c2c1c4832db322e19b314206d
2015-10-30 18:38:42 +01:00
Marko Mikulicic e46185068b Avoid inlining of esp gcc bug workaround
PUBLISHED_FROM=f02283b42d842246c75df848061a80b754a97013
2015-10-30 18:38:42 +01:00
Marko Mikulicic 6ef2fea45e Workaround lx106 gcc bug stack frame size rounding
PUBLISHED_FROM=66906d0e5f6d97b135bd157f5f82dc13bef2fb1a
2015-10-30 18:38:41 +01:00
Deomid Ryabkov e244dd84a9 Mongoose and LWIP fixes
PUBLISHED_FROM=c63992029098108ae62234176d4334207b43dd7b
2015-10-30 18:38:41 +01:00
Deomid Ryabkov 5034937a15 Set more reasonable WS buffer size
PUBLISHED_FROM=ecfc284747f1b3eef5c40dd74b4af19f4618edcc
2015-10-30 18:38:41 +01:00
Deomid Ryabkov 060045be65 Un-static a bunch of structs to save ram when idle
PUBLISHED_FROM=1a994be80dd46ffd410f88db28816c85b2c2bf79
2015-10-30 18:38:41 +01:00
Deomid Ryabkov 9adfe8d484 espconn is out, Mongoose + LWIP is in
PUBLISHED_FROM=c67ea324058d70070f89420c40e57b9df87964b1
2015-10-30 18:38:41 +01:00
Deomid Ryabkov 799cc369e0 Reduce stack usage when parsing HTTP URL
PUBLISHED_FROM=682f59e58d8335352cc09987a91e85cc264cf0f8
2015-10-30 18:38:41 +01:00
Deomid Ryabkov b97d3fea4b strnlen should return size_t
PUBLISHED_FROM=545c7d80380c48332803ef00981763b130a1c1c6
2015-10-30 18:38:40 +01:00
Deomid Ryabkov 2a4ca9d5c3 Introduce a network interface API, refactor UDP
PUBLISHED_FROM=6e961e2760b2b64e211978ede5df8ca353ea5512
2015-10-30 18:38:40 +01:00
Deomid Ryabkov 747e40e74e Fix size_t printing on CC3200
PUBLISHED_FROM=65b068a7620e7117c0dffc762d710e08dfbad037
2015-10-30 18:38:40 +01:00
Marko Mikulicic cf279e7885 Address ignored return values of read and write
PUBLISHED_FROM=d76b6b9d91d7af0e916cd81e1213c463757bf924
2015-10-30 18:38:39 +01:00
Marko Mikulicic cbf968735a Fix MG_SEND_FUNC/MG_RECV_FUNC rename fail
PUBLISHED_FROM=da28a75111abb5fb49b122133a66ae9bdf63f474
2015-10-30 18:38:39 +01:00
Sergey Lyubka 57b35e6007 Add JS HTTP server to smartjs
PUBLISHED_FROM=13210e55d9434917549dcbf9a39a5377181f8387
2015-10-21 17:34:55 +02:00
Deomid Ryabkov 1eaabdd8e5 s/dmg/dns/g
PUBLISHED_FROM=96dc8b0b4001f2206749a401c615f043fc5f6131
2015-10-21 15:17:11 +02:00
Sergey Lyubka dcd4e660a3 Serve current dir by default in mg_serve_http()
PUBLISHED_FROM=421b4256629b2bccd20ceb127ece02be57085b40
2015-10-21 15:17:11 +02:00
Deomid Ryabkov 96409e74e5 Fix ALL the leaks!
PUBLISHED_FROM=0adf7c6c903075d6705693829f8758f8ff67d579
2015-10-21 15:17:11 +02:00
Sergey Lyubka e28ee097a1 Move mongoose binary to dev repo
PUBLISHED_FROM=224a216abc4a7cca50d80f762dca602f54470105
2015-10-21 15:17:11 +02:00
Deomid Ryabkov 2b4e544867 Move "connect done" logic from mg_read_from_socket
PUBLISHED_FROM=318cc110d8b012cf75cb02dfc68a8215faa93fe7
2015-10-21 15:17:11 +02:00
Deomid Ryabkov ee042a3481 Fix SSI parsing
PUBLISHED_FROM=c918da948354def5815dfd6b832053f170212c6f
2015-10-16 12:57:02 +01:00
Alexander Alashkin 63075f6846 Add restful_client & restful_server VC projects
PUBLISHED_FROM=5ee78f5c907401c93a04f89d6b25b99515aa809a
2015-10-16 10:31:23 +01:00
Alexander Alashkin 7f2e42a06d Fix MSVC2015 build and add VC project
PUBLISHED_FROM=d071ecd0bdcc871cdd18a65c1ebb8b546ff61941
2015-10-16 10:31:23 +01:00
Sergey Lyubka 3fbfa33b9a Minor nitpicks for big_upload
PUBLISHED_FROM=55d4270916f32ce4645965fbd7a031d7f8e1907b
2015-10-13 10:38:36 +01:00
Maxim Ignatenko 179aa75047 Add big_upload example
PUBLISHED_FROM=58dfd3cfdbc5f908e5fa10171274f0d5dd256404
2015-10-13 10:38:36 +01:00
Maxim Ignatenko 4658194d08 Properly populate mg_connection.sa field
PUBLISHED_FROM=a305c720aee1b159847e10c962b6de7fe08bc787
2015-10-12 12:48:35 +01:00
Marko Mikulicic b35657814c Factor our LWIP enabling flag
PUBLISHED_FROM=d080ccb2d0daef1103ff7487b5c3f4b65ab97b99
2015-10-12 11:05:33 +01:00
Marko Mikulicic a2322c77c6 Fix mongoose build
PUBLISHED_FROM=ec4a774dfd690876201080ff55bdbec5bd865670
2015-10-12 11:05:33 +01:00
Marko Mikulicic b50c183c19 Simplify and abstract C func arg access
PUBLISHED_FROM=c174439a2d22794497dc35fd46ceb1f6c4e553a2
2015-10-12 11:05:32 +01:00
Maxim Ignatenko a941687db0 Fix mongoose build
PUBLISHED_FROM=aec35861339d336697649fd2020766439b3814ad
2015-10-12 11:05:32 +01:00
Marko Mikulicic c2eef52b65 Add CLA to mongoose README
PUBLISHED_FROM=9987c788e55bc949c06ebf355d51d38ef2d109d6
2015-10-12 11:05:32 +01:00
Claudiu 3e6cbbde04 Fix static function prototype
PUBLISHED_FROM=d90d06593ec4531f479053243c72601ee27a3141
2015-10-05 22:27:10 +02:00
Sergey Lyubka 6400536282 Rename v7_get_global_object() -> v7_get_global()
PUBLISHED_FROM=91a306d5dbc55d65505904b1a19cca5bbe9f58cc
2015-10-05 22:27:10 +02:00
Sergey Lyubka 776103068f Add examples for mongoose blog article
PUBLISHED_FROM=54f54211919bb2276e02b4d10306ffa5540a9313
2015-10-02 13:10:47 +01:00
Maxim Ignatenko 93beff3b65 Use select() by default everywhere
PUBLISHED_FROM=474789b0d7f5076434e5c15c079306243f3a8d81
2015-09-25 16:19:50 +01:00
Marko Mikulicic 22f8d86b05 Makes sure send file releases resources on close
PUBLISHED_FROM=451d2dd0b55b9fd7dea441a9279f54d0005c97b1
2015-09-25 16:19:50 +01:00
Marko Mikulicic 3a9a9c7ad0 Enable printf format warnings for mg_printf
PUBLISHED_FROM=61a52651a2542cb89b155623781a5fcbf64779f3
2015-09-25 16:19:50 +01:00
Alexander Alashkin 13238b1ad2 Restore win build
PUBLISHED_FROM=fe072db3dcc5f92f44d91a81596e33e404887951
2015-09-25 16:19:50 +01:00
Marko Mikulicic 56f661e9c6 Make mg send_http_file work for ESP8266 RTOS
PUBLISHED_FROM=10623cfe38e143f7e107a6494baa298c788586f0
2015-09-25 16:19:49 +01:00
Marko Mikulicic 5351f46efb Mongoose send on esp8266 RTOS_SDK
PUBLISHED_FROM=1e97853ebc1c39b01754cf76d875453842e9b36d
2015-09-25 16:19:49 +01:00
Sergey Lyubka 0cd3335b78 Update mongoose download link
PUBLISHED_FROM=70a6e34c6630e3ab95d200b173d22529101b61ea
2015-09-22 10:35:55 +01:00
Marko Mikulicic 0611627448 Serve a http file on quantex
PUBLISHED_FROM=856d2e0bdaddece638d215c252fabeb9ea2d7809
2015-09-22 10:32:02 +01:00
Sergey Lyubka f014918435 Add JS support to Mongoose
PUBLISHED_FROM=5f319c5c6d965432da063ce7ab495ae24e972755
2015-09-22 10:32:01 +01:00
Sergey Lyubka 51e016089e Nitpick README update
PUBLISHED_FROM=28b3a30ad82432e8e3ca194d7e204acf9a998e94
2015-09-22 10:32:01 +01:00
Sergey Lyubka 8a66ff5278 Mongoose rebrand changes
PUBLISHED_FROM=50497043e8d0c10487804263cdcb1cbb260d5a26
2015-09-21 15:19:35 +01:00
Sergey Lyubka e1dd3f06fe Rename Mongoose constants: NS_ -> MG_, NSF_ -> MG_F_
PUBLISHED_FROM=c9cc54df1883aa17606de2b1ffb30f0cd687d037
2015-09-21 15:19:34 +01:00
Deomid Ryabkov 32ab35f61f Rename hmac_sha1 -> cs_hmac_sha1
PUBLISHED_FROM=3d4fbd8192d04add406e304b113ce5ec0b1c1165
2015-09-21 15:19:34 +01:00
Marko Mikulicic 8ab96ec74b Implement binary and incremental clubby client
PUBLISHED_FROM=ba371b8a3d9dd8afcb771144beb8e5c344c7d6c7
2015-09-21 15:19:33 +01:00
Sergey Lyubka 0730590e35 Fix SSL codepath in restful_server example
PUBLISHED_FROM=4cd52839b8d2396f50f94f1ce36fa7b4501dc204
2015-09-21 15:19:33 +01:00
Marko Mikulicic f7e0b1331c Implement incremental UBJSON serializer
PUBLISHED_FROM=fe0936cbf8c16d5b5cfdf674e8e7181f6b2771cc
2015-09-21 15:19:33 +01:00
Marko Mikulicic 17aa2a7092 Implement streaming base64 encoder
PUBLISHED_FROM=af3c6c0e8f9806c1bdc570c06cc6eea9995d6e3d
2015-09-21 15:19:33 +01:00
Alexander Alashkin 7d38d05ca7 Use updated sockets
PUBLISHED_FROM=30231da54720c709ce98cd0ab208cf9b06814eab
2015-09-21 15:19:33 +01:00
TheTypoMaster 33bad047f0 Fix typo recieve
PUBLISHED_FROM=6e2f4fffb91ff18da94d97aba763acbdde496515
2015-09-09 10:21:21 +01:00
Marko Mikulicic 5234b73c29 Add simple core dumper for ESP
PUBLISHED_FROM=ec98516ce6aa1c841344adc7aae20b044b3d349b
2015-09-09 10:21:21 +01:00
Sergey Lyubka c566e2eba5 Mongoose README
PUBLISHED_FROM=f2409461d1ef4eef81b5481914bee5e596e906f8
2015-09-08 14:42:13 +02:00
Marko Mikulicic e565977481 Merge pull request #568 from cesanta/fossamerge
Merge dev branch code named Fossa as next stable Mongoose
2015-09-08 14:39:52 +02:00
Marko Mikulicic 8927c9d22b Merge dev branch code named Fossa as next stable Mongoose 2015-09-08 14:34:30 +02:00
Marko Mikulicic 28eb251ccd Merge pull request #567 from cesanta/move
Fossa merge
2015-09-08 13:28:38 +02:00
Sergey Lyubka b99dc95b1d Fossa merge.
PUBLISHED_FROM=12ccba10358075024a1195cd0b0c1ea132aae6a3
2015-09-08 12:26:25 +01:00
Sergey Lyubka 75aadb3e79 Fix links 2015-08-26 11:23:54 +01:00
Sergey Lyubka b6ecd5fb4c Update readme 2015-08-20 10:20:02 +01:00
Sergey Lyubka 1e7f55ed84 Update readme 2015-08-20 08:56:14 +01:00
Sergey Lyubka eeadc5ecff Updated readme 2015-08-18 12:21:33 +01:00
Sergey Lyubka 568ca42988 Updated readme 2015-08-18 11:26:43 +01:00
Sergey Lyubka 7b758bfc9d Updated readme 2015-08-18 10:47:23 +01:00
Sergey Lyubka 8ef454b75f Updated readme 2015-08-18 09:50:00 +01:00
Marko Mikulicic 0a2f2fda5c Merge pull request #563 from cesanta/sdp
Tweak event handler a bit for clarity
2015-08-13 15:42:45 +02:00
rojer 6b440918ec Tweak event handler a bit for clarity
Put cases in the order we expect them to occur
2015-08-13 14:38:41 +01:00
Marko Mikulicic a73fce945c Merge pull request #562 from cesanta/sdp
Swallow responses to internal pings
2015-08-12 19:45:04 +02:00
rojer d349bb3cc7 Swallow responses to internal pings 2015-08-12 18:35:18 +01:00
Marko Mikulicic 7c4119a287 Merge pull request #561 from cesanta/sdp
Mongoose example for server data push
2015-08-12 18:52:51 +02:00
rojer 8a71ad99e6 Mongoose example for server data push 2015-08-12 17:42:05 +01:00
Sergey Lyubka 41b405d7bf Remove examples/web_server 2015-07-13 11:14:19 +01:00
Sergey Lyubka 30b80dc5ee Update example 2015-07-13 11:11:26 +01:00
Sergey Lyubka 524aa2e586 Merge pull request #556 from nesteroff/vs2013
Fix VS2013 warnings
2015-07-13 11:03:13 +01:00
Yuri Nesterov 990a1b9ae0 Fix VS2013 warnings 2015-07-12 15:43:54 +03:00
Sergey Lyubka 4cb0c6864e Merge pull request #550 from cesanta/fixpolar
Fix ssl close with polarssl
2015-07-02 17:06:30 +01:00
Marko Mikulicic 5183693951 Fix ssl close with polarssl 2015-07-02 16:26:31 +02:00
Sergey Lyubka e47506f957 Fix unauthenticated body handling 2015-06-22 12:05:31 +01:00
Sergey Lyubka 824e4dc0e4 Fix file upload iteration 2015-06-19 19:01:21 +01:00
Sergey Lyubka d033497714 Merge pull request #541 from NickCis/connection_param_fix
Possible fix in order not to loose the reference of connection_param pointer
2015-06-10 15:32:28 +01:00
Nicolas Cisco d8028df29d Quick fix in order not to loose the reference of connection_param pointer 2015-06-10 01:49:37 -03:00
Sergey Lyubka 564683d25a Fix timeout for cookie auth example 2015-06-05 18:09:27 +01:00
Sergey Lyubka 30c0a3f201 Propagate select() and socketpair() errors to the user 2015-06-04 11:27:31 +01:00
Sergey Lyubka 08fe9ac518 Fix examples 2015-05-20 10:53:10 +01:00
Sergey Lyubka 14d6f71f82 Fix unit test 2015-05-20 10:50:32 +01:00
Sergey Lyubka 29454e2296 Merge pull request #524 from NickCis/array_variables
Implementing array varibles
2015-05-20 10:05:48 +01:00
NickCis d6cbf9f77d Fixing C coding style mistakes 2015-05-19 13:54:35 -03:00
Sergey Lyubka b9fead4d6e Merge pull request #525 from NickCis/mg_vprintf
Adding mg_vprintf and mg_vprintf_data.
2015-05-19 15:54:27 +01:00
Sergey Lyubka c6a04e1719 Fix #523 2015-05-18 11:50:56 +01:00
NickCis c839a61fea Adding mg_vprintf and mg_vprintf_data in order to use va_list like functions 2015-05-15 16:20:33 -03:00
NickCis c06abbc5f7 Implementing array varibles 2015-05-15 15:49:35 -03:00
Sergey Lyubka c8e88e1710 Sync fossa struct definitions 2015-05-15 17:20:54 +01:00
Sergey Lyubka bf7a115bf2 Fix url rewrites 2015-05-11 09:13:46 +01:00
Sergey Lyubka b8dfb69cf5 Merge pull request #514 from cesanta/fix_setsize
Fix default windows FD_SETSIZE, closes #506
2015-04-17 07:10:24 +01:00
Marko Mikulicic 1cd9715095 Fix default windows FD_SETSIZE, closes #506
https://support.microsoft.com/en-us/kb/111855 describes
that the user is expected to set the FD_SETSIZE macro
before including winsock2.h
2015-04-17 02:30:22 +02:00
Sergey Lyubka 5c7aeac148 Merge pull request #509 from cesanta/Fix_mjpg_streamer_example
Fix mjpg_streamer example
2015-04-13 23:37:27 +01:00
Alexander Alashkin 09a80628e6 Fix mjpg_streamer example 2015-04-13 16:02:52 +03:00
Sergey Lyubka 2ff2c369f7 Merge pull request #505 from eugoss/master
Inspect POST buffer in mg_get_var() only if first call of get_var() returns -1
2015-04-10 18:31:15 +01:00
Marko Mikulicic eef70aaf74 Merge pull request #507 from gitter-badger/gitter-badge
Add a Gitter chat badge to README.md
2015-04-09 13:04:18 +02:00
The Gitter Badger 89ac97caba Added Gitter badge 2015-04-09 10:58:41 +00:00
Eugene Ossintsev c00b236c8d Fix spelling in docs/ReleaseNotes.md and in comments in mongoose.c 2015-04-09 00:15:40 -04:00
Eugene Ossintsev eacd3f35e0 Inspect POST buffer in mg_get_var() only if first call of get_var() returns -1
In mg_get_var() the first call of get_var() inspects the variables of
the query string. If the requested variable is found but the destination
buffer is too small to hold the variable, return -2 right away. If it's
not found, make the second call of get_var() to inspect the POST buffer.
2015-04-05 23:40:50 -04:00
Marko Mikulicic 8a2aa72571 Merge pull request #504 from himanshupsharma/check_fd-setsize
Reverting (socket < 0) checks to avoid Windows build failure.
2015-04-03 11:22:50 +02:00
p_himanshu 7da8f273d2 Reverting (socket < 0) checks to avoid Windows build failure. 2015-04-02 16:47:07 -07:00
Sergey Lyubka a90a5a2b6a Merge pull request #503 from himanshupsharma/check_fd-setsize
Adding a check to avoid undefined behavior if socket is out of the bound...
2015-04-02 18:28:23 +01:00
p_himanshu b5b819b4a3 Adding a check to avoid undefined behavior if socket is out of the bounds of FD_SETSIZE. 2015-04-01 22:24:29 -07:00
Sergey Lyubka 821e2b3a75 Merge pull request #501 from xant/upstream
safety belt : scan_directory() can return without setting arr to a valid value
2015-04-01 20:22:20 +01:00
Sergey Lyubka 773efb0745 Respect WEBSOCKET_OPCODE_CONNECTION_CLOSE coming from client 2015-04-01 18:37:13 +01:00
Andrea Guzzo ed1904a75d safety belt : scan_directory() can return without setting arr to a valid value 2015-03-31 12:52:08 +02:00
Sergey Lyubka a66663f0f6 Fix non-openssl build 2015-03-31 10:38:14 +01:00
Sergey Lyubka 1104688b02 Fix win32 build no2 2015-03-25 17:29:34 +00:00
Sergey Lyubka 36d95e230e Fix win32 build 2015-03-25 17:10:58 +00:00
Sergey Lyubka 8a27a95a2a Fix #460 2015-03-24 09:32:16 +00:00
Sergey Lyubka 2e63b5a5e5 Fix #441 2015-03-24 08:04:46 +00:00
Sergey Lyubka 4cbd113d01 Upload multiple files 2015-03-24 07:52:49 +00:00
Sergey Lyubka ba5ae74144 Merge pull request #495 from mmicko/master
OS2 and Android NDK fixes
2015-03-22 17:03:28 +00:00
Miodrag Milanovic f2b40e5fae added comment for time_t change 2015-03-22 17:56:22 +01:00
Sergey Lyubka 8ed682930b Fix #448 2015-03-21 13:17:24 +00:00
Miodrag Milanovic 86ad7922b0 OS2 and Android NDK fixes
Note that in Android NDK st_mtime is long unsigned int
2015-03-21 11:04:57 +01:00
Sergey Lyubka f7476f53c7 Merge pull request #493 from RobertDiPaolo/bugfix/Visual_Studios_Build_Issues
Correctly handle _CRT_SECURE_NO_WARNINGS already being set in project se...
2015-03-20 16:29:11 +00:00
Sergey Lyubka 5d6f7ac8ef Merge pull request #494 from RobertDiPaolo/bugfix/Memory_Leak
Fixed Memory Leak
2015-03-20 16:28:15 +00:00
Sergey Lyubka f16fa5ed55 Merge pull request #492 from RobertDiPaolo/feature/Add_PATCH_Method
Added PATCH to list of valid HTTP Methods.
2015-03-20 16:28:11 +00:00
Sergey Lyubka c0ba32f7a6 Set NS_DISABLE_THREADS ifndef MONGOOSE_ENABLE_THREADS 2015-03-20 15:33:02 +00:00
Sergey Lyubka 409c12a851 Better logic for port resolving 2015-03-18 17:25:46 +00:00
Sergey Lyubka bcdcbf20ee Release 5.6 2015-03-18 17:25:31 +00:00
Sergey Lyubka 8fc5b5a55f Add contribution section to readme 2015-03-18 15:45:52 +00:00
Robert Di Paolo 7cf28a5623 The 'char *ip' variable was being allocated and zeroed but not being used or freed. 2015-03-18 10:04:08 +00:00
Robert Di Paolo 6668c456ac Correctly handle _CRT_SECURE_NO_WARNINGS already being set in project settings. 2015-03-18 09:56:01 +00:00
Robert Di Paolo 98f2476751 Added PATCH to list of valid HTTP Methods. 2015-03-18 09:48:17 +00:00
Sergey Lyubka faeedb2930 Sync main 2015-03-18 09:37:52 +00:00
Sergey Lyubka c8e8f94ffe Sync main 2015-03-17 17:35:31 +00:00
Sergey Lyubka 22aeb6111b Update ReleaseNotes 2015-03-17 17:01:16 +00:00
Sergey Lyubka 2d1d2e6534 SIZE_MAX is not defined on MSVC6, use (size_t) ~0 2015-03-17 17:00:59 +00:00
Sergey Lyubka 05c88a98fc Introduce -dav_root option 2015-03-17 15:34:56 +00:00
Marko Mikulicic 5224afb946 Merge pull request #485 from dsrosario/fix_remove_double_dots 2015-03-17 16:07:46 +01:00
Dinis Rosário 3bd79c7cd5 Fix remove_double_dots_and_double_slashes removing all the dots leading http server to serve wrong URI
If the uri is something like '/js/...jquery.js', remove_double_dots_and_double_slashes will remove the 3 dots and the http server will serves the /js/jquery.js file.
remove_double_dots_and_double_slashes should check if a dot or double dots is followed by a slash (or backslash) and only remove this to avoid disclosure attack.
2015-03-17 16:04:59 +01:00
Sergey Lyubka c52e0744cb Squashed warnings after size_t change 2015-03-17 13:18:15 +00:00
Sergey Lyubka 6c09c303d0 Merge pull request #456 from sebgod/fix-win32-examples-using-extra
Enable building of example/Makefile on Win32
2015-03-17 11:45:36 +00:00
Sergey Lyubka b6fde06990 Merge pull request #486 from mmicko/master
Compile fix
2015-03-16 16:52:48 +00:00
Miodrag Milanovic af892c9726 compile fix for visual studio 2015-03-14 09:02:25 +01:00
Miodrag Milanovic df7ad13680 Compile fix 2015-03-13 20:15:19 +01:00
Sergey Lyubka 057c2d4b42 Merge pull request #484 from davidmilligan/master
fix compiler warnings for 64 bit compilation
2015-03-10 18:40:16 +00:00
David Milligan 6c9b43bcf5 fix compiler warnings for 64 bit compilation 2015-03-09 20:01:09 -04:00
Sergey Lyubka a91438acae Fix #477 2015-03-09 16:09:05 +00:00
Sergey Lyubka a6fa51dbc2 Merge pull request #481 from cesanta/web_server_ssl
web_server_ssl
2015-03-06 14:39:50 +00:00
Alexander Alashkin 6ceac6c84c web_server_ssl 2015-03-06 15:08:57 +02:00
Sergey Lyubka 288efe3c23 Add -DMONGOOSE_ENABLE_THREADS to other examples 2015-03-06 09:29:56 +00:00
Sergey Lyubka 5207545a8b Add -DMONGOOSE_ENABLE_THREADS to proxy server example 2015-03-06 09:28:27 +00:00
Sergey Lyubka 716d524f9b Add -DMONGOOSE_ENABLE_THREADS to Makefile 2015-03-06 09:25:30 +00:00
Sergey Lyubka a434bab767 Merge pull request #480 from cesanta/polarssl_examples_1
websocket_ssl_proxy sample for polarsll
2015-03-06 09:03:38 +00:00
Alexander Alashkin e9633a8d2d websocket_ssl_proxy 2015-03-06 09:32:08 +02:00
Sergey Lyubka c7b6a6f0ea Added mg_websocket_printf() 2015-03-05 10:54:51 +00:00
Sergey Lyubka 6cdb9f7ed3 Merge pull request #474 from mmicko/master
OS/2 patches by KO Myung-Hun
2015-03-04 17:18:16 +00:00
Sergey Lyubka 4502dd0c86 s/MONGOOSE_NO_THREADS/MONGOOSE_ENABLE_THREADS 2015-03-03 18:59:23 +00:00
Sergey Lyubka 95055f6048 Add winsock2.h include for win32 2015-03-03 18:32:14 +00:00
Sergey Lyubka 2c36532ec4 Merge pull request #465 from doug16rogers/vs9-compile-ipv6
Allow for compilation in Visual Studio with NS_ENABLE_IPV6.
2015-03-03 18:28:07 +00:00
Sergey Lyubka f2b0b9c737 Fix Win32 build in mg_websocket_write() 2015-02-24 13:32:22 +00:00
Sergey Lyubka b3866439a0 Fix mg_create_server() signature 2015-02-24 13:17:11 +00:00
Miodrag Milanovic 4f2205a667 OS/2 patches by KO Myung-Hun
Upstream patch from MAME project
2015-02-24 11:12:09 +01:00
Sergey Lyubka 6cec1d36a8 Merge pull request #463 from CurlyMoo/patch-1
Replace obsolete gethostbyname by getaddrinfo
2015-02-19 11:48:35 +00:00
CurlyMoo 4238370ae1 Check for getaddrinfo availability. 2015-02-19 12:09:34 +01:00
Sergey Lyubka b4924b715e Merge pull request #473 from mmicko/master
Fix for MINGW builds
2015-02-17 16:19:37 +00:00
Miodrag Milanovic 7a0385dcac Fix for MINGW builds 2015-02-17 11:08:23 +01:00
Sergey Lyubka 44a146faf6 SSL fixes 2015-02-16 18:34:07 +00:00
Sergey Lyubka 53a55da7bc Add type cast in ns_out() 2015-02-16 18:19:15 +00:00
Sergey Lyubka bf44277764 Define _XOPEN_SOURCE for MinGW build 2015-02-16 18:17:30 +00:00
Sergey Lyubka a2e1da6e30 Add overflow checks 2015-02-12 19:12:58 +00:00
Sergey Lyubka bef3f85ba8 Merge pull request #466 from 0-wiz-0/master
Only define _XOPEN_SOURCE on Linux.
2015-02-10 18:07:07 +00:00
Thomas Klausner eb72a63cca Only define _XOPEN_SOURCE on Linux.
Fixes the build on NetBSD.

The comment says it's only needed on Linux, and AFAIK Linux
is the only operating system that only adds symbols when it's defined;
others, like NetBSD or Solaris, also hide symbols.

Signed-off-by: Thomas Klausner <wiz@NetBSD.org>
2015-01-29 15:02:26 +01:00
Doug Rogers f19bbc73cc Allow for compilation in Visual Studio 9 (2008) with NS_ENABLE_IPV6. 2015-01-29 00:12:35 -05:00
Sergey Lyubka 25326b3b12 net skeleton -> fossa 2015-01-28 12:33:48 +00:00
CurlyMoo 10ee430bfd Add debug line and adapt to styling guide
Replaced tabs with spaces.
2015-01-27 22:55:13 +01:00
CurlyMoo 10d0bfdab4 Replace obsolete gethostbyname by getaddrinfo
This also protect against the latest glibc bug
2015-01-27 22:45:34 +01:00
Sergey Lyubka 6b6637c337 Adopt SSL guide to current way of setting SSL cert. 2015-01-21 17:41:45 +00:00
Sergey Lyubka 878acca213 Removed Lua docs 2015-01-21 17:30:30 +00:00
Sebastian Godelet 8082501b9c Enable building of example/Makefile on Win32
Adding example/.gitignore to ignore *.exe
Appending -lws2_32 at the end of CFLAGS_EXTRA if running make on
Windows to enable easy compiling under that platform.
Obsoletes pull request #454
2015-01-10 16:41:58 +08:00
179 changed files with 165867 additions and 9735 deletions

No files matched your search

+5 -5
View File
@@ -2,15 +2,15 @@ Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
Copyright (c) 2013-2015 Cesanta Software Limited Copyright (c) 2013-2015 Cesanta Software Limited
All rights reserved All rights reserved
This code is dual-licensed: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation. For the terms of this 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 You are free to use this software under the terms of the GNU General
Public License, but WITHOUT ANY WARRANTY; without even the implied Public License, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details. See the GNU General Public License for more details.
Alternatively, you can license this code under a commercial Alternatively, you can license this software under a commercial
license, as set out in <http://cesanta.com/>. license, as set out in <https://www.cesanta.com/license>.
+82 -66
View File
@@ -1,88 +1,104 @@
# <img src="http://cesanta.com/images/mongoose_logo.png" width="64" height="64"> Mongoose Web Server # <img src="http://www.cesanta.com/hubfs/www.cesanta.com/Images/mongoose_library.png" width="64" height="64"> Mongoose - Embedded 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. ![](https://img.shields.io/badge/license-GPL_2-green.svg "License")
Mongoose is built on top of Libmongoose embedded library, which can turn Mongoose is ideal for embedded environments. It has been designed
anything into a web server in 5 minutes worth of effort and few lines of code. for connecting devices and bringing them online.
Libmongoose is used to serve Web GUI on embedded devices, implement RESTful [Mongoose](https://www.cesanta.com/products)
services, RPC frameworks (e.g. JSON-RPC), handle telemetry data exchange, and has been on the market since 2004 and attracted
perform many other tasks in various different industries including aerospace, over 1 million cumulative downloads.
manufacturing, finance, research, automotive, gaming, IT.
It provides easy to use event-driven interface that allows to implement
network protocols or scalable network applications with little effort.
Mongoose helps developers to manage the complexity of network programming
and let them concentrate on the logic, saving time and money.
* [Mailing list](http://groups.google.com/group/mongoose-users) - [Download Mongoose Source Code here](http://hubs.ly/H01bWvx0)
* [Downloads](http://cesanta.com/products.shtml) - [Ask questions on support forum](http://forum.cesanta.com/index.php?p=/categories/mongoose)
* [Documentation](http://cesanta.com/docs.shtml) - [Read User Guide and API reference](https://docs.cesanta.com/mongoose)
- [Study mongoose example code](https://github.com/cesanta/mongoose/tree/master/examples)
> Nothing overdone. Nothing less. So unbelievably easy to use.
> Just how good software should be! -- Pritin Tyagaraj, SAP
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 # Features
- Works on Windows, Mac, UNIX/Linux, iPhone, Android eCos, QNX * Cross-platform: works on Linux/UNIX, MacOS, QNX, eCos, Windows, Android,
and many other platforms iPhone, FreeRTOS (TI CC3200, ESP8266), etc
- CGI, SSI, SSL, Digest auth, Websocket, WEbDAV, Resumed download, * Single-threaded, asynchronous, non-blocking core with simple event-based API
URL rewrite, file blacklist * Native support for [PicoTCP embedded TCP/IP stack](http://www.picotcp.com),
- Custom error pages, Virtual hosts, IP-based ACL, Windows service, [LWIP embedded TCP/IP stack](https://en.wikipedia.org/wiki/LwIP)
HTTP/HTTPS client * Builtin protocols:
- Simple and clean - plain TCP, plain UDP, SSL/TLS (over TCP, one-way or two-way)
[embedding API](https://github.com/cesanta/mongoose/blob/master/mongoose.h). - HTTP client, HTTP server
The source is in single - Websocket client, Websocket server
[mongoose.c](https://github.com/cesanta/mongoose/blob/master/mongoose.c) file - JSON-RPC client, JSON-RPC server
to make embedding easy - MQTT client, MQTT broker
- Extremely lightweight, has a core of under 40kB and tiny runtime footprint - CoAP client, CoAP server
- Asynchronous, non-blocking core supporting single- or multi-threaded usage - DNS client, DNS server, async DNS resolver
- On the market since 2004 with over 1 million cumulative downloads * Tiny static and run-time footprint
- Stable, mature and tested, has several man-years invested * Source code is both ISO C and ISO C++ compliant
in continuous improvement and refinement * Very easy to integrate: just copy
[mongoose.c](https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.c) and
[mongoose.h](https://raw.githubusercontent.com/cesanta/mongoose/master/mongoose.h)
files to your build tree
* Extensively tested and production-ready, trusted by many blue chip businesses
# Screenshots # Dashboard Example
Download, double-click to start, run browser -- that's all! Mongoose is often used to implement device dashboards and real-time
data exchange over Websocket. Here is a dashboard example that illustrates
the functionality:
![shot1](http://cesanta.com/images/tut_sharing/tut1.png) ![](http://www.cesanta.com/hubfs/www.cesanta.com/diagrams/dash_mongoose_diagram.png)
![shot2](http://cesanta.com/images/tut_sharing/tut2.png)
![shot3](http://cesanta.com/images/tut_sharing/tut3.png) [Developing a new product? Contact us today to discuss how Mongoose can help
![shot4](http://cesanta.com/images/tut_sharing/tut4.png) ](https://www.cesanta.com/contact)
# Acknowledgements # Contributions
Mongoose made better thanks to the contribution of following people: People who have agreed to the
[Cesanta CLA](https://docs.cesanta.com/contributors_la.shtml)
Arnout Vandecappelle, Benoît Amiaux, Boris Pek, Cody Hanson, Colin Leitner, can make contributions. Note that the CLA isn't a copyright
Daniel Oaks, Eric Bakan, Erik Oomen, Filipp Kovalev, Ger Hobbelt, _assigment_ but rather a copyright _license_.
Hendrik Polczynski, Igor Okulist, Jay, Joe Mucchiello, John Safranek, You retain the copyright on your contributions.
José Miguel Gonçalves, Shueng Chuan, Katerina Blinova, Konstantin Sorokin,
Marin Atanasov, Matt Healy, Mitch Hendrickson, Nigel Stewart, Pavel Khlebovich,
Sebastian Reinhard, Stefan Doehla, abadc0de, nullable.type,
T.Barmann, D.Hughes, J.C.Sloan, R.Romeo, L.E.Spencer, S.Kotay, R.M.Shorter,
W.Mar, J.Wilander, Santa from Memphis, S.Davies, C.Beck,
O.M.Vilhunen, C.Radik, G.Woodcock, M.Szczepkowski,
Eternal Lands Dev Team, T.Tollet, C.Tangerino, G.Karsai, A.Bourgett,
C.Blakemore, D.Fonaryov, T.Andrle, O.IJsselmuiden, R.Womack, M.Tomlinson,
A.Slåttå, L.Farrell, J.D.P.Ballestero, V.Albaev, B.Harker, T.Scheffel, H.Klein,
R.Merit, T.Bennett, H.Solis, A.Zincenko, M.S., S.Krul, K.Cooke, S.McCallum,
F.Morenius, and 10 others.
# Licensing # Licensing
Mongoose is released under commercial and You are using Mongoose under
[GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) open [GPLv2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
source licenses. The GPLv2 open source License does not generally permit and until you commercialize your
incorporating this software into non-open source programs. application, can continue to use Mongoose free of charge.
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.
# Other products by Cesanta Software: simple and effective Once you commercialize your product, in order to comply with GPLv2
you need to either open the source code of your product fully or
alternatively purchase a commercial license.
We offer [three levels of commercial licensing](https://www.cesanta.com/products):
- [SSL Wrapper](https://github.com/cesanta/ssl_wrapper) - application to - Mongoose Lite: cross platform integration, SSL/TLS, HTTP support
secure network communications - Mongoose: Mongoose Lite + HTTP CGI, HTTP SSI, HTTP Digest Auth,
WebSocket support
- Mongoose Premium: Mongoose + Plain TCP, UDP, JSON-RPC, MQTT, DNS,
CoAP support
[Enquire about commercial licensing here](https://www.cesanta.com/contact)
# Mongoose Binary
This is our easy to use web server for web developers (PHP, Ruby, Python, etc)
and web designers. Available in three editions to suit your needs: free, pro
(USD 5) and dev edition (from USD 8). To install, simply download, double-click
to start and run browser - that's all!
[Download Mongoose Binary here](https://www.cesanta.com/products#binary)
# Other products by Cesanta
- [Smart.js](https://github.com/cesanta/smart.js) -
Generic, hardware independent, full-stack IoT software platform
- [V7](https://github.com/cesanta/v7) - Embedded JavaScript engine
- [Frozen](https://github.com/cesanta/frozen) - JSON parser and generator - [Frozen](https://github.com/cesanta/frozen) - JSON parser and generator
- [SLRE](https://github.com/cesanta/slre) - Super Light Regular Expression - [SLRE](https://github.com/cesanta/slre) - Super Light Regular Expression
library library
- [Net Skeleton](https://github.com/cesanta/net_skeleton) - framework for
building network applications
- [SLDR](https://github.com/cesanta/sldr) - Super Light DNS Resolver
-246
View File
@@ -1,246 +0,0 @@
# Mongoose API Reference
struct mg_server *mg_create_server(void *server_param);
Creates web server instance. Returns opaque instance pointer, or NULL if
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.
Note that this function doesn't make the
server instance to serve. Serving is done by `mg_poll_server()` function.
Mongoose has single-threaded, event-driven, asynchronous, non-blocking core.
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 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
calling `mg_create_server()`.
Important: Mongoose does not install `SIGCHLD` handler. If CGI is used,
`SIGCHLD` handler must be set up to reap CGI zombie processes.
void mg_destroy_server(struct mg_server **server);
Deallocates web server instance, closes all pending connections, and makes
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. 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. `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()` (Mongoose thread) and change server configuration while it
is serving, in between `mg_poll_server()` calls.
int mg_poll_server(struct mg_server *server, int milliseconds);
Performs one iteration of IO loop by iterating over all
active connections, performing `select()` syscall on all sockets with a timeout
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 only single thread
(Mongoose thread) should make Mongoose calls.
`mg_poll_server()` calls user-specified event handler when certain events
occur. Sequence of events for the accepted 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. Event handler return value is ignored.
Sequence of events for the client connection is this:
* `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 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
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.
Return value: number of active connections.
const char **mg_get_valid_option_names(void);
Returns a NULL-terminated array of option names and their default values.
There are two entries per option in an array: an option name followed by a
default value. A default value could be NULL. A NULL name indicates an end
of the array.
const char *mg_get_option(const struct mg_server *server, const char *name);
Returns the value of particular configuration parameter. If
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, ...);
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,
const char *value);
void mg_send_data(struct mg_connection *, const void *data, int data_len);
void mg_printf_data(struct mg_connection *, const char *format, ...);
These functions are used to construct a response to the client. HTTP response
consists of three parts: a status line, zero or more HTTP headers,
a response body. Mongoose provides functions for all three parts:
* `mg_send_status()` is used to create status line. This function can be
called zero or once. If `mg_send_status()` is not called, then Mongoose
will send status 200 (success) implicitly.
* `mg_send_header()` adds HTTP header to the response. This function could
be called zero or more times.
* `mg_send_data()` and `mg_printf_data()` are used to send data to the
client. Note that Mongoose adds `Transfer-Encoding: chunked` header
implicitly, and sends data in chunks. Therefore, it is not necessary to
set `Content-Length` header. Note that `mg_send_data()` and
`mg_printf_data()` do not send data immediately. Instead, they spool
data in memory, and Mongoose sends that data later after URI handler
returns. If data to be sent is huge, an URI handler might
send data in pieces by saving state in
`struct mg_connection::connection_param` variable and returning `0`. Then
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);
Similar to `mg_write()`, but wraps the data into a websocket frame with a
given websocket `opcode`.
const char *mg_get_header(const struct mg_connection *, const char *name);
Get the value of particular HTTP header. This is a helper function.
It traverses http_headers array, and if the header is present in the array,
returns its value. If it is not present, NULL is returned.
int mg_get_var(const struct mg_connection *conn, const char *var_name,
char *buf, size_t buf_len);
Gets HTTP form variable. Both POST buffer and query string are inspected.
Form variable is url-decoded and written to the buffer. On success, this
function returns the length of decoded variable. On error, -1 is returned if
variable not found, and -2 is returned if destination buffer is too small
to hold the variable. Destination buffer is guaranteed to be
'\0' - terminated if it is not NULL or zero length.
int mg_parse_header(const char *hdr, const char *var_name, char *buf,
size_t buf_size);
This function parses HTTP header and fetches given variable's value in a buffer.
A header should be like `x=123, y=345, z="other value"`. This function is
designed to parse Cookie headers, Authorization headers, and similar. Returns
the length of the fetched value, or 0 if variable not found.
int mg_modify_passwords_file(const char *passwords_file_name,
const char *domain,
const char *user,
const char *password);
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.
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.
-27
View File
@@ -1,27 +0,0 @@
# Mongoose Build on Android
This is a small guide to help you run mongoose on Android. Currently it is
tested on the HTC Wildfire. If you have managed to run it on other devices
as well, please comment or drop an email in the mailing list.
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`
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.
- From adb shell, navigate to `/data/local` and execute `./mongoose`.
- 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](http://cesanta.com/images/android_build.png)
Notes:
- `jni` stands for Java Native Interface. Read up on Android NDK if you want
to know how to interact with the native C functions of mongoose in Android
Java applications.
- TODO: A Java application that interacts with the native binary or a
shared library.
-34
View File
@@ -1,34 +0,0 @@
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)
-173
View File
@@ -1,173 +0,0 @@
# Mongoose Embedding Guide
Embedding Mongoose is done in two steps:
1. Copy
[mongoose.c](https://raw.github.com/cesanta/mongoose/master/mongoose.c) and
[mongoose.h](https://raw.github.com/cesanta/mongoose/master/mongoose.h)
to your application's source tree and include them in the build.
2. Somewhere in the application code, call `mg_create_server()` to create
a server, configure it with `mg_set_option()` and loop with
`mg_poll_server()` until done. Call `mg_destroy_server()` to cleanup.
Here's a minimal application `app.c` that embeds mongoose:
#include "mongoose.h"
int main(void) {
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 `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 -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.
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. `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
`/hello` URI by showing a hello message. So, when app is run,
http://127.0.0.1:8080/hello will say hello, and here's the code:
#include <string.h>
#include "mongoose.h"
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, event_handler);
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
}
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
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
(PUT, DELETE, MKCOL, PROPFIND methods)
-DMONGOOSE_NO_DIRECTORY_LISTING Disable directory listing
-DMONGOOSE_NO_FILESYSTEM Disables all file IO, serving from memory only
-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_LUA Enable Lua scripting
-DMONGOOSE_USE_LUA_SQLITE3 Enable sqlite3 binding for Lua
-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
-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
-45
View File
@@ -1,45 +0,0 @@
# Mongoose FAQ
## 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
full path to the PHP interpreter, e.g.:
mongoose -cgi_interpreter /full/path/to/php-cgi
## Mongoose fails to start
If Mongoose exits immediately when run, this
usually indicates a syntax error in the configuration file
(named `mongoose.conf` by default) or the command-line arguments.
Syntax checking is omitted from Mongoose to keep its size low. However,
the Manual should be of help. Note: the syntax changes from time to time,
so updating the config file might be necessary after executable update.
### Embedding with OpenSSL on Windows might fail because of calling convention
To force Mongoose to use `__stdcall` convention, add `/Gz` compilation
flag to the Visual Studio project settings.
-18
View File
@@ -1,18 +0,0 @@
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)
-44
View File
@@ -1,44 +0,0 @@
# Mongoose Internals
Mongoose has single-threaded, event-driven, asynchronous, non-blocking core.
`mg_create_server()` creates a web server instance. An instance is a container
for the config options and list of active connections. To do the actual
serving, user must call `mg_poll_server()`, which iterates over all
active connections, performing `select()` syscall on all sockets with a
timeout of specified number 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.
Mongoose server instance is designed to be used by a single thread.
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).
On a multi-core systems, many server instances can be created, sharing the
same listening socket and managed by separate threads (see [multi_threaded.c](https://github.com/cesanta/mongoose/blob/master/examples/multi_threaded.c))
example.
It is an error to pass and store `struct mg_connection *` pointers for
later use to send data. The reason is that they can be invalidated by the
next `mg_poll_server()` call. For such a task,
there is `mg_iterate_over_connections()` API
exists, which sends a callback function to the IO thread, then IO thread
calls specified function for all active connection.
When mongoose buffers in HTTP request and successfully parses it, it calls
appropriate URI handler immediately for GET requests. 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
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
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()` should be used.
-51
View File
@@ -1,51 +0,0 @@
# Mongoose Lua Server Pages
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.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 `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
`&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:
<? mg.write('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n') ?>
<html><body>
<span>Today is:</span> <? mg.write(os.date("%A")) ?>
</body></html>
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 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 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:
mg.write(str) -- writes string to the client
mg.onerror(msg) -- error handler, can be overridden
mg.request_info -- a table with request information
Using Lua scripting it is easy to emulate SSI functionality. For example,
to include the content of another file, one can write:
<? mg.write(io.open('MY_FILE.TXT'):read('*all')) ?>
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 all following blocks.
-154
View File
@@ -1,154 +0,0 @@
# Mongoose Configuration Options
### access\_control\_list
An Access Control List (ACL) allows restrictions to be put on the list of IP
addresses which have access to the web server. In the case of the Mongoose
web server, the ACL is a comma separated list of IP subnets, where each
subnet is prepended by either a `-` or a `+` sign. A plus sign means allow,
where a minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`,
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 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.
### access\_log\_file
Path to a file for access logs. Either full path, or relative to the
mongoose executable. Default: not set, no query logging is done.
### auth_domain
Authorization realm used in `.htpasswd` authorization. Default: `mydomain.com`
### cgi_interpreter
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 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**: 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 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$`
### dav\_auth\_file
Authentication file for WebDAV mutation requests: `PUT`, `DELETE`, `MKCOL`.
The format of that file is the same as for the `.htpasswd` file
used for digest authentication. It can be created and managed by
`mongoose -A` command. Default: not set, WebDAV mutations are disallowed.
### document_root
A directory to serve. Default: current working directory.
### enable\_directory\_listing
Enable directory listing, either `yes` or `no`. Default: `yes`.
### 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,...`.
Extension must include dot. Example:
`mongoose -extra_mime_types .cpp=plain/text,.java=plain/text`. Default: not set.
### global\_auth\_file
Path to a global passwords file, either full path or relative to the mongoose
executable. If set, per-directory `.htpasswd` files are ignored,
and all requests are authorised against that file. Use `mongoose -A` to
manage passwords, or third party utilities like
[htpasswd-generator](http://www.askapache.com/online-tools/htpasswd-generator).
Default: not set, per-directory `.htpasswd` files are respected.
### hide\_files\_patterns
A pattern for the files to hide. Files that match the pattern will not
show up in directory listing and return `404 Not Found` if requested. Pattern
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.
### 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`
### 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. 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_port 80 -run_as_user nobody
Default: not set.
### url\_rewrites
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. 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_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 with following parameters:
`?code=HTTP_ERROR_CODE&orig_uri=ORIGINAL_URI&query_string=QUERY_STRING`.
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
# Implement user home directories support
mongoose -url_rewrites /~joe/=/home/joe/,/~bill=/home/bill/
# 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
@@ -1,49 +0,0 @@
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
@@ -1,193 +0,0 @@
# 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)
-75
View File
@@ -1,75 +0,0 @@
# Mongoose SSL guide
SSL is a protocol that makes web communication secure. To enable SSL
in mongoose, 3 steps are required:
1. Valid certificate file must be created
2. `ssl_certificate` options must be set to contain path to the
certificate file.
3. `listening_ports` option must contain a port number with letter `s`
appended to it, which instructs Mongoose to use SSL for all connections
made to that port.
Below is the `mongoose.conf` file snippet for typical SSL setup:
document_root www_root # Serve files in www_root directory
listening_ports 80r,443s # Redirect all HTTP requests to HTTPS
ssl_certificate ssl_cert.pem # Location of certificate file
## How to create SSL certificate file
SSL certificate file is a text file that must contain at least two
sections:
1. A private key
2. A certificate
Both sections should be chunks of text in PEM format. When PEM file is
opened in a text editor, it looks like this:
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAwONaLOP7EdegqjRuQKSDXzvHmFMZfBufjhELhNjo5KsL4ieH
hYN0Zii2yTb63jGxKY6gH1R/r9dL8kXaJmcZrfSa3AgywnteJWg=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDBjCCAe4CCQCX05m0b053QzANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJB
SEGI4JSxV56lYg==
-----END CERTIFICATE-----
Two aforementioned sections are clearly seen. Typically, those section
are bigger then in the example shown. The text between the `BEGIN` and
`END` is the text representation of binary data, a private key and a
certificate. Therefore, in order to create a certificate file,
* private key must be converted to PEM format
* certificate must be converted to PEM format
* those two should be concatenated into a single file
If the certificate chain in used, a chain file also needs to be
converted into PEM format and appended to the certificate file.
## How SSL works
SSL is a protocol that can encrypt communication between two parties. If third
party observes all messages passed by, it would be very
hard for the third party (though not impossible) to decrypt the communication.
The idea is based on so-called public key encryption. Communicating parties
have two keys: a public key and a private key. A public key is advertised
to everybody, and it is contained in a certificate. A private key is kept
secret. Security algorithm works in a way that anybody can encrypt
a message using public key, and only private key can decrypt it.
This is why web server needs both private key and certificate: private key
is used to decrypt incoming messages, and certificate is used to tell the
public key to the other party. When communication starts, parties exchange
their public keys, and keep private keys to themselves. Man-in-the-middle
who observes the communication is unable to decrypt the messages cause
private keys are required for decryption.
Encryption algorithms are built on top of hard mathematical problem, which
makes it very expensive for man-in-the-middle to compute private keys.
For example, RSA algorithm is based on a mathematical problem of factorization.
It is easy to generate two very large prime numbers `P` and `Q` and make
a product `P * Q`. But given a product, it is very hard to recover these
two prime numbers - this is called factorization.
-86
View File
@@ -1,86 +0,0 @@
# Mongoose User Guide
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 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 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
terminal, optionally followed by configuration parameters
(`mongoose [OPTIONS]`) or configuration file name
(`mongoose [config_file_name]`) starts the
web server:
$ mongoose -document_root /var/www # Running mongoose with cmdline options
$ mongoose /etc/my_config.txt # Running mongoose with config file
$ mongoose # Running with no parameters. This will
# serve current directory on port 8080
Mongoose does not detach from terminal. Pressing `Ctrl-C` keys
stops the server.
When started, mongoose first searches for the configuration file.
If configuration file is specified explicitly in the command line, then
specified configuration file is used.
Otherwise, mongoose would search for file `mongoose.conf` in the same directory
where binary is located, and use it. Configuration file can be absent.
Configuration file is a sequence of lines, each line containing
command line argument name and it's value. Empty lines and lines beginning
with `#` are ignored. Here is the example of `mongoose.conf` file:
# This is a comment
document_root C:\www
listening_port 80
ssl_certificate C:\mongoose\ssl_cert.pem
Command line arguments are highest priority and can override
configuration file settings. For example, if `mongoose.conf` has line
`document_root /var/www`, and mongoose has been started as
`mongoose -document_root /etc`, then `/etc` directory will be used as
document root.
Note that configuration options on the command line must start with `-`,
and their names are the same as in the config file. Exampli gratia,
the following two setups are equivalent:
$ mongoose -listening_port 1234 -document_root /var/www
$ cat > mongoose.conf
listening_ports 1234
document_root /var/www
^D
$ mongoose
Mongoose can also be used to modify `.htpasswd` passwords file:
$ mongoose -A .htpasswd mydomain.com user_name user_password
Unlike other web servers, mongoose does not require CGI scripts be located in
a special directory. CGI scripts can be anywhere. CGI (and SSI) files are
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.
All other characters in the pattern match themselves. Examples:
# 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:
$ mongoose -cgi_pattern /cgi-bin/*.cgi # Emulate /cgi-bin/ restriction
+1
View File
@@ -0,0 +1 @@
*.exe
+118
View File
@@ -0,0 +1,118 @@
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of object file images to be generated ()
# GEN_BINS - list of binaries to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
TARGET = eagle
#FLAVOR = release
FLAVOR = debug
EXTRA_CCFLAGS += -Wall -Werror
ifndef PDIR # {
GEN_IMAGES= eagle.app.v6.out
GEN_BINS= eagle.app.v6.bin
SPECIAL_MKTARGETS=$(APP_MKTARGETS)
SUBDIRS=user
endif # } PDIR
LDDIR = $(SDK_PATH)/ld
CCFLAGS += -Os
TARGET_LDFLAGS = \
-nostdlib \
-Wl,-EL \
--longcalls \
--text-section-literals
ifeq ($(FLAVOR),debug)
TARGET_LDFLAGS += -g -O2
endif
ifeq ($(FLAVOR),release)
TARGET_LDFLAGS += -g -O0
endif
COMPONENTS_eagle.app.v6 = \
user/libuser.a
LINKFLAGS_eagle.app.v6 = \
-L$(SDK_PATH)/lib \
-Wl,--gc-sections \
-nostdlib \
-T$(LD_FILE) \
-Wl,--no-check-sections \
-u call_user_start \
-Wl,-static \
-Wl,--start-group \
-lcirom \
-lgcc \
-lhal \
-lphy \
-lpp \
-lnet80211 \
-lwpa \
-lcrypto \
-lmain \
-lfreertos \
-llwip \
$(DEP_LIBS_eagle.app.v6) \
-Wl,--end-group
DEPENDS_eagle.app.v6 = \
$(LD_FILE) \
$(LDDIR)/eagle.rom.addr.v6.ld
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
# Generally values applying to a tree are captured in the
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
#UNIVERSAL_TARGET_DEFINES = \
# Other potential configuration flags include:
# -DTXRX_TXBUF_DEBUG
# -DTXRX_RXBUF_DEBUG
# -DWLAN_CONFIG_CCX
CONFIGURATION_DEFINES = -DICACHE_FLASH
DEFINES += \
$(UNIVERSAL_TARGET_DEFINES) \
$(CONFIGURATION_DEFINES)
DDEFINES += \
$(UNIVERSAL_TARGET_DEFINES) \
$(CONFIGURATION_DEFINES)
#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
# corresponding to the common APIs applicable to modules
# rooted at that subtree. Accordingly, the INCLUDE PATH
# of a module can only contain the include directories up
# its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#
INCLUDES := $(INCLUDES) -I $(PDIR)include
sinclude $(SDK_PATH)/Makefile
.PHONY: FORCE
FORCE:
+36
View File
@@ -0,0 +1,36 @@
This is a Mongoose "Hello, world" that can be compiled under ESP8266 RTOS SDK.
It sets up an AP (SSID `Mongoose`) and serves a "hello world" page on http://192.168.4.1/
Most of the the boilerplate comes from [project_template](https://github.com/espressif/ESP8266_RTOS_SDK/tree/master/examples/project_template) (@ [3ca6af5](https://github.com/espressif/ESP8266_RTOS_SDK/tree/3ca6af5da68678d809b34c7cd98bee71e0235039/examples/project_template)) with minimal changes.
To build with no changes to the SDK, you will need a module with 1MB (8Mb) flash or more.
Compile (for NodeMCU 1.0):
```
$ export SDK_PATH=/path/to/ESP8266_RTOS_SDK
$ export BIN_PATH=./bin
$ make clean; make BOOT=new APP=1 SPI_SPEED=40 SPI_MODE=dio SPI_SIZE_MAP=6
```
Flash (using [esptool](https://github.com/themadinventor/esptool)):
```
$ esptool.py --port /dev/ttyUSB0 --baud 230400 \
write_flash --flash_mode=dio --flash_size=32m \
0x00000 ${SDK_PATH}/bin/boot_v1.4\(b1\).bin \
0x01000 ${BIN_PATH}/upgrade/user1.4096.new.6.bin
```
The output can be made to fit in 512KB (4Mb), but stock linker scripts do not reserve enough space for code.
Custom linker script is provided for that, so you can use it for smaller devices like so (example that will work with ESP-01):
```
$ make clean; make BOOT=none APP=0 SPI_SPEED=40 SPI_MODE=qio SPI_SIZE_MAP=0 LD_FILE=ld/eagle.app.v6.512.compact.ld
$ esptool.py --port /dev/ttyUSB0 --baud 230400 \
write_flash --flash_mode=qio --flash_size=4m \
0x00000 ${BIN_PATH}/eagle.flash.bin \
0x10000 ${BIN_PATH}/eagle.irom0text.bin
```
+173
View File
@@ -0,0 +1,173 @@
#!/bin/bash
:<<!
******NOTICE******
MUST set SDK_PATH & BIN_PATH firstly!!!
example:
export SDK_PATH=~/ESP8266_RTOS_SDK
export BIN_PATH=~/esp8266_bin
!
echo "gen_misc.sh version 20150911"
echo ""
if [ $SDK_PATH ]; then
echo "SDK_PATH:"
echo "$SDK_PATH"
echo ""
else
echo "ERROR: Please export SDK_PATH in gen_misc.sh firstly, exit!!!"
exit
fi
if [ $BIN_PATH ]; then
echo "BIN_PATH:"
echo "$BIN_PATH"
echo ""
else
echo "ERROR: Please export BIN_PATH in gen_misc.sh firstly, exit!!!"
exit
fi
echo "Please check SDK_PATH & BIN_PATH, enter (Y/y) to continue:"
read input
if [[ $input != Y ]] && [[ $input != y ]]; then
exit
fi
echo ""
echo "Please follow below steps(1-5) to generate specific bin(s):"
echo "STEP 1: use boot_v1.2+ by default"
boot=new
echo "boot mode: $boot"
echo ""
echo "STEP 2: choose bin generate(0=eagle.flash.bin+eagle.irom0text.bin, 1=user1.bin, 2=user2.bin)"
echo "enter (0/1/2, default 0):"
read input
if [ -z "$input" ]; then
if [ $boot != none ]; then
boot=none
echo "ignore boot"
fi
app=0
echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
elif [ $input == 1 ]; then
if [ $boot == none ]; then
app=0
echo "choose no boot before"
echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
else
app=1
echo "generate bin: user1.bin"
fi
elif [ $input == 2 ]; then
if [ $boot == none ]; then
app=0
echo "choose no boot before"
echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
else
app=2
echo "generate bin: user2.bin"
fi
else
if [ $boot != none ]; then
boot=none
echo "ignore boot"
fi
app=0
echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
fi
echo ""
echo "STEP 3: choose spi speed(0=20MHz, 1=26.7MHz, 2=40MHz, 3=80MHz)"
echo "enter (0/1/2/3, default 2):"
read input
if [ -z "$input" ]; then
spi_speed=40
elif [ $input == 0 ]; then
spi_speed=20
elif [ $input == 1 ]; then
spi_speed=26.7
elif [ $input == 3 ]; then
spi_speed=80
else
spi_speed=40
fi
echo "spi speed: $spi_speed MHz"
echo ""
echo "STEP 4: choose spi mode(0=QIO, 1=QOUT, 2=DIO, 3=DOUT)"
echo "enter (0/1/2/3, default 0):"
read input
if [ -z "$input" ]; then
spi_mode=QIO
elif [ $input == 1 ]; then
spi_mode=QOUT
elif [ $input == 2 ]; then
spi_mode=DIO
elif [ $input == 3 ]; then
spi_mode=DOUT
else
spi_mode=QIO
fi
echo "spi mode: $spi_mode"
echo ""
echo "STEP 5: choose spi size and map"
echo " 0= 512KB( 256KB+ 256KB)"
echo " 2=1024KB( 512KB+ 512KB)"
echo " 3=2048KB( 512KB+ 512KB)"
echo " 4=4096KB( 512KB+ 512KB)"
echo " 5=2048KB(1024KB+1024KB)"
echo " 6=4096KB(1024KB+1024KB)"
echo "enter (0/2/3/4/5/6, default 0):"
read input
if [ -z "$input" ]; then
spi_size_map=0
echo "spi size: 512KB"
echo "spi ota map: 256KB + 256KB"
elif [ $input == 2 ]; then
spi_size_map=2
echo "spi size: 1024KB"
echo "spi ota map: 512KB + 512KB"
elif [ $input == 3 ]; then
spi_size_map=3
echo "spi size: 2048KB"
echo "spi ota map: 512KB + 512KB"
elif [ $input == 4 ]; then
spi_size_map=4
echo "spi size: 4096KB"
echo "spi ota map: 512KB + 512KB"
elif [ $input == 5 ]; then
spi_size_map=5
echo "spi size: 2048KB"
echo "spi ota map: 1024KB + 1024KB"
elif [ $input == 6 ]; then
spi_size_map=6
echo "spi size: 4096KB"
echo "spi ota map: 1024KB + 1024KB"
else
spi_size_map=0
echo "spi size: 512KB"
echo "spi ota map: 256KB + 256KB"
fi
echo ""
echo "start..."
echo ""
make clean
make BOOT=$boot APP=$app SPI_SPEED=$spi_speed SPI_MODE=$spi_mode SPI_SIZE_MAP=$spi_size_map
@@ -0,0 +1,20 @@
/* eagle.flash.bin @ 0x00000 */
/* eagle.irom0text.bin @ 0x10000 */
/*
* Flash map for 512KB flash.
*
* There is 0x8000 (32KB) available between 0x8000 and 0x10000.
*
* Note: IROM images starts at 0x10000 (ignore Makefile output which says 0x40000.
*/
MEMORY
{
dport0_0_seg : org = 0x3FF00000, len = 0x10
dram0_0_seg : org = 0x3FFE8000, len = 0x18000
iram1_0_seg : org = 0x40100000, len = 0x8000
irom0_0_seg : org = 0x40210000, len = 0x6C000
}
INCLUDE "../ld/eagle.app.v6.common.ld"
+56
View File
@@ -0,0 +1,56 @@
This is a simple project template.
sample_lib is an example for multi-level folder Makefile, notice the folder structure and each Makefile, you can get the clue.
HOWTO:
1. Copy this folder to anywhere.
Example:
Copy to ~/workspace/project_template
You can rename this folder as you like.
2. Export SDK_PATH and BIN_PATH.
Example:
Your SDK path is ~/esp_iot_rtos_sdk, and want generate bin at ~/esp8266_bin.
Do follow steps:
1>. export SDK_PATH=~/esp_iot_rtos_sdk
2>. export BIN_PATH=~/esp8266_bin
SDK and project are seperate, you can update SDK without change your project.
3. Enter project_template folder, run ./gen_misc.sh, and follow the tips and steps.
Compile Options:
(1) COMPILE
Possible value: xcc
Default value:
If not set, use gcc by default.
(2) BOOT
Possible value: none/old/new
none: no need boot
old: use boot_v1.1
new: use boot_v1.2
Default value: new
(3) APP
Possible value: 0/1/2
0: original mode, generate eagle.app.v6.flash.bin and eagle.app.v6.irom0text.bin
1: generate user1
2: generate user2
Default value: 0
(3) SPI_SPEED
Possible value: 20/26.7/40/80
Default value: 40
(4) SPI_MODE
Possible value: QIO/QOUT/DIO/DOUT
Default value: QIO
(4) SPI_SIZE_MAP
Possible value: 0/2/3/4/5/6
Default value: 0
For example:
make COMPILE=gcc BOOT=new APP=1 SPI_SPEED=40 SPI_MODE=QIO SPI_SIZE_MAP=0
+47
View File
@@ -0,0 +1,47 @@
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of images to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
ifndef PDIR
GEN_LIBS = libuser.a
endif
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
# Generally values applying to a tree are captured in the
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
DEFINES += -DMG_DISABLE_DAV -DMG_DISABLE_SYNC_RESOLVER -DMG_DISABLE_CGI \
-DMG_DISABLE_SOCKETPAIR -DMG_DISABLE_DIRECTORY_LISTING \
-DMG_MAX_HTTP_HEADERS=20 -DMG_MAX_HTTP_REQUEST_SIZE=1024 \
-DMG_MAX_PATH=40 -DMG_MAX_HTTP_SEND_IOBUF=1024 \
-DMG_ESP8266 -DRTOS_SDK -DMG_LWIP
#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
# corresponding to the common APIs applicable to modules
# rooted at that subtree. Accordingly, the INCLUDE PATH
# of a module can only contain the include directories up
# its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#
INCLUDES += -I ./
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile
+54
View File
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*/
#include <ctype.h>
#include <sys/time.h>
#include <stdint.h>
#include <math.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "esp_common.h"
/* Makes fprintf(stdout) and stderr work. */
_ssize_t _write_r(struct _reent *r, int fd, void *buf, size_t len) {
if (fd == 1 || fd == 2) {
size_t i;
for (i = 0; i < len; i++) {
os_putc(((char *) buf)[i]);
}
return len;
}
return -1;
}
/*
* You'll need to implement _open_r and friends if you want file operations. See
* https://github.com/cesanta/smart.js/blob/master/platforms/esp8266/user/esp_fs.c
* for SPIFFS-based implementation.
*/
void abort(void) __attribute__((no_instrument_function));
void abort(void) {
/* cause an unaligned access exception, that will drop you into gdb */
*(int *) 1 = 1;
while (1)
; /* avoid gcc warning because stdlib abort() has noreturn attribute */
}
void _exit(int status) {
printf("_exit(%d)\n", status);
abort();
}
int _gettimeofday_r(struct _reent *r, struct timeval *tp, void *tzp) {
uint32_t time = system_get_time();
tp->tv_sec = time / 1000000;
tp->tv_usec = time % 1000000;
return 0;
}
+1
View File
@@ -0,0 +1 @@
../../../mongoose.c
+1
View File
@@ -0,0 +1 @@
../../../mongoose.h
+108
View File
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*/
#include "esp_common.h"
#include "mongoose.h"
#define AP_SSID "Mongoose"
#define AP_PASS "Mongoose"
#define AP_CHAN 9
#define MG_LISTEN_ADDR "80"
#define MG_TASK_STACK_SIZE 2048
#define MG_TASK_PRIORITY 1
void uart_div_modify(int uart_no, unsigned int freq);
void ev_handler(struct mg_connection *nc, int ev, void *p) {
static const char *reply_fmt =
"HTTP/1.0 200 OK\r\n"
"Connection: close\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
"Hello %s\n";
LOG(LL_DEBUG, ("conn %p ev %d", nc, ev));
switch (ev) {
case MG_EV_ACCEPT: {
char addr[32];
mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr),
MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
LOG(LL_INFO, ("Connection %p from %s", nc, addr));
break;
}
case MG_EV_HTTP_REQUEST: {
char addr[32];
struct http_message *hm = (struct http_message *) p;
mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr),
MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
LOG(LL_INFO,
("HTTP request from %s: %.*s %.*s", addr, (int) hm->method.len,
hm->method.p, (int) hm->uri.len, hm->uri.p));
mg_printf(nc, reply_fmt, addr);
nc->flags |= MG_F_SEND_AND_CLOSE;
break;
}
case MG_EV_CLOSE: {
LOG(LL_INFO, ("Connection %p closed", nc));
break;
}
}
}
void setup_ap() {
struct softap_config cfg;
wifi_set_opmode_current(SOFTAP_MODE);
memset(&cfg, 0, sizeof(cfg));
strcpy((char *) cfg.ssid, AP_SSID);
strcpy((char *) cfg.password, AP_PASS);
cfg.ssid_len = strlen((const char *) cfg.ssid);
cfg.authmode =
strlen((const char *) cfg.password) ? AUTH_WPA2_PSK : AUTH_OPEN;
cfg.channel = AP_CHAN;
cfg.ssid_hidden = 0;
cfg.max_connection = 10;
cfg.beacon_interval = 100; /* ms */
LOG(LL_INFO, ("Setting up AP '%s' on channel %d", cfg.ssid, cfg.channel));
wifi_softap_set_config_current(&cfg);
}
static void mg_task(void *arg) {
struct mg_mgr mgr;
struct mg_connection *nc;
cs_log_set_level(LL_INFO);
LOG(LL_INFO, ("SDK version: %s", system_get_sdk_version()));
setup_ap();
mg_mgr_init(&mgr, NULL);
nc = mg_bind(&mgr, MG_LISTEN_ADDR, ev_handler);
if (nc == NULL) {
LOG(LL_ERROR, ("Error setting up listener!"));
return;
}
mg_set_protocol_http_websocket(nc);
while (1) {
mg_mgr_poll(&mgr, 1000);
}
}
xTaskHandle s_mg_task_handle;
void user_init(void) {
uart_div_modify(0, UART_CLK_FREQ / 115200);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
xTaskCreate(mg_task, (const signed char *) "mongoose", MG_TASK_STACK_SIZE,
NULL, MG_TASK_PRIORITY, &s_mg_task_handle);
}
+2 -2
View File
@@ -1,7 +1,7 @@
# Copyright (c) 2014 Cesanta Software # Copyright (c) 2014 Cesanta Software
# All rights reserved # All rights reserved
SUBDIRS = $(sort $(filter-out csharp/, $(dir $(wildcard */)))) SUBDIRS = $(sort $(dir $(wildcard */)))
X = $(SUBDIRS) X = $(SUBDIRS)
.PHONY: $(SUBDIRS) .PHONY: $(SUBDIRS)
@@ -12,4 +12,4 @@ $(SUBDIRS):
@$(MAKE) -C $@ @$(MAKE) -C $@
clean: clean:
for d in $(SUBDIRS) ; do $(MAKE) -C $$d clean ; done for d in $(SUBDIRS) ; do $(MAKE) -C $$d clean ; done
+25
View File
@@ -0,0 +1,25 @@
PROG = api_server
SOURCES = $(PROG).c sqlite3.c db_plugin_sqlite.c ../../mongoose.c
CFLAGS = -W -Wall -pthread $(CFLAGS_EXTRA)
ifeq ($(OS), Windows_NT)
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S), Linux)
CFLAGS += -ldl -lm
endif
endif
all: $(PROG)
$(PROG): $(SOURCES)
$(CC) $(SOURCES) -o $@ $(CFLAGS)
$(PROG).exe: $(SOURCES)
cl $(SOURCES) /I.. /MD /Fe$@
test: $(PROG)
sh unit_test.sh $$(pwd)/$(PROG)
clean:
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
+104
View File
@@ -0,0 +1,104 @@
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#include "db_plugin.h"
static const char *s_http_port = "8000";
static struct mg_serve_http_opts s_http_server_opts;
static int s_sig_num = 0;
static void *s_db_handle = NULL;
static const char *s_db_path = "api_server.db";
static const struct mg_str s_get_method = MG_STR("GET");
static const struct mg_str s_put_method = MG_STR("PUT");
static const struct mg_str s_delele_method = MG_STR("DELETE");
static void signal_handler(int sig_num) {
signal(sig_num, signal_handler);
s_sig_num = sig_num;
}
static int has_prefix(const struct mg_str *uri, const struct mg_str *prefix) {
return uri->len > prefix->len && memcmp(uri->p, prefix->p, prefix->len) == 0;
}
static int is_equal(const struct mg_str *s1, const struct mg_str *s2) {
return s1->len == s2->len && memcmp(s1->p, s2->p, s2->len) == 0;
}
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
static const struct mg_str api_prefix = MG_STR("/api/v1");
struct http_message *hm = (struct http_message *) ev_data;
struct mg_str key;
switch (ev) {
case MG_EV_HTTP_REQUEST:
if (has_prefix(&hm->uri, &api_prefix)) {
key.p = hm->uri.p + api_prefix.len;
key.len = hm->uri.len - api_prefix.len;
if (is_equal(&hm->method, &s_get_method)) {
db_op(nc, hm, &key, s_db_handle, API_OP_GET);
} else if (is_equal(&hm->method, &s_put_method)) {
db_op(nc, hm, &key, s_db_handle, API_OP_SET);
} else if (is_equal(&hm->method, &s_delele_method)) {
db_op(nc, hm, &key, s_db_handle, API_OP_DEL);
} else {
mg_printf(nc, "%s",
"HTTP/1.0 501 Not Implemented\r\n"
"Content-Length: 0\r\n\r\n");
}
} else {
mg_serve_http(nc, hm, s_http_server_opts); /* Serve static content */
}
break;
default:
break;
}
}
int main(int argc, char *argv[]) {
struct mg_mgr mgr;
struct mg_connection *nc;
int i;
/* Open listening socket */
mg_mgr_init(&mgr, NULL);
nc = mg_bind(&mgr, s_http_port, ev_handler);
mg_set_protocol_http_websocket(nc);
s_http_server_opts.document_root = "web_root";
/* Parse command line arguments */
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-D") == 0) {
mgr.hexdump_file = argv[++i];
} else if (strcmp(argv[i], "-f") == 0) {
s_db_path = argv[++i];
} else if (strcmp(argv[i], "-r") == 0) {
s_http_server_opts.document_root = argv[++i];
}
}
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
/* Open database */
if ((s_db_handle = db_open(s_db_path)) == NULL) {
fprintf(stderr, "Cannot open DB [%s]\n", s_db_path);
exit(EXIT_FAILURE);
}
/* Run event loop until signal is received */
printf("Starting RESTful server on port %s\n", s_http_port);
while (s_sig_num == 0) {
mg_mgr_poll(&mgr, 1000);
}
/* Cleanup */
mg_mgr_free(&mgr);
db_close(&s_db_handle);
printf("Exiting on signal %d\n", s_sig_num);
return 0;
}
+19
View File
@@ -0,0 +1,19 @@
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#ifndef DB_PLUGIN_HEADER_DEFINED
#define DB_PLUGIN_HEADER_DEFINED
#include "../../mongoose.h"
void *db_open(const char *db_path);
void db_close(void **db_handle);
enum { API_OP_GET, API_OP_SET, API_OP_DEL };
void db_op(struct mg_connection *nc, const struct http_message *hm,
const struct mg_str *key, void *db, int op);
#endif /* DB_PLUGIN_HEADER_DEFINED */
+107
View File
@@ -0,0 +1,107 @@
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#include "db_plugin.h"
#include "sqlite3.h"
void *db_open(const char *db_path) {
sqlite3 *db = NULL;
if (sqlite3_open_v2(db_path, &db, SQLITE_OPEN_READWRITE |
SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) == SQLITE_OK) {
sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS kv(key PRIMARY KEY, val);",
0, 0, 0);
}
return db;
}
void db_close(void **db_handle) {
if (db_handle != NULL && *db_handle != NULL) {
sqlite3_close(*db_handle);
*db_handle = NULL;
}
}
static void op_set(struct mg_connection *nc, const struct http_message *hm,
const struct mg_str *key, void *db) {
sqlite3_stmt *stmt = NULL;
char value[200];
const struct mg_str *body = hm->query_string.len > 0 ?
&hm->query_string : &hm->body;
mg_get_http_var(body, "value", value, sizeof(value));
if (sqlite3_prepare_v2(db, "INSERT OR REPLACE INTO kv VALUES (?, ?);",
-1, &stmt, NULL) == SQLITE_OK) {
sqlite3_bind_text(stmt, 1, key->p, key->len, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, value, strlen(value), SQLITE_STATIC);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
}
static void op_get(struct mg_connection *nc, const struct http_message *hm,
const struct mg_str *key, void *db) {
sqlite3_stmt *stmt = NULL;
const char *data = NULL;
int result;
(void) hm;
if (sqlite3_prepare_v2(db, "SELECT val FROM kv WHERE key = ?;",
-1, &stmt, NULL) == SQLITE_OK) {
sqlite3_bind_text(stmt, 1, key->p, key->len, SQLITE_STATIC);
result = sqlite3_step(stmt);
data = (char *) sqlite3_column_text(stmt, 0);
if ((result == SQLITE_OK || result == SQLITE_ROW) && data != NULL) {
mg_printf(nc, "HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: %d\r\n\r\n%s",
(int) strlen(data), data);
} else {
mg_printf(nc, "%s", "HTTP/1.1 404 Not Found\r\n"
"Content-Length: 0\r\n\r\n");
}
sqlite3_finalize(stmt);
} else {
mg_printf(nc, "%s", "HTTP/1.1 500 Server Error\r\n"
"Content-Length: 0\r\n\r\n");
}
}
static void op_del(struct mg_connection *nc, const struct http_message *hm,
const struct mg_str *key, void *db) {
sqlite3_stmt *stmt = NULL;
int result;
(void) hm;
if (sqlite3_prepare_v2(db, "DELETE FROM kv WHERE key = ?;",
-1, &stmt, NULL) == SQLITE_OK) {
sqlite3_bind_text(stmt, 1, key->p, key->len, SQLITE_STATIC);
result = sqlite3_step(stmt);
if (result == SQLITE_OK || result == SQLITE_ROW) {
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
} else {
mg_printf(nc, "%s", "HTTP/1.1 404 Not Found\r\n"
"Content-Length: 0\r\n\r\n");
}
sqlite3_finalize(stmt);
} else {
mg_printf(nc, "%s", "HTTP/1.1 500 Server Error\r\n"
"Content-Length: 0\r\n\r\n");
}
}
void db_op(struct mg_connection *nc, const struct http_message *hm,
const struct mg_str *key, void *db, int op) {
switch (op) {
case API_OP_GET: op_get(nc, hm, key, db); break;
case API_OP_SET: op_set(nc, hm, key, db); break;
case API_OP_DEL: op_del(nc, hm, key, db); break;
default:
mg_printf(nc, "%s", "HTTP/1.0 501 Not Implemented\r\n"
"Content-Length: 0\r\n\r\n");
break;
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+35
View File
@@ -0,0 +1,35 @@
#!/bin/sh
PROG=$1
PORT=${2:-8000} # If second param is given, this is load balancer port
DB_FILE=/tmp/_$$.db
URL=http://127.0.0.1:$PORT/api/v1
cleanup() {
rm -rf $DB_FILE
kill -9 $PID >/dev/null 2>&1
}
#set -x
trap cleanup EXIT
cleanup
$PROG -f $DB_FILE &
PID=$!
#sleep 1
curl -s -X PUT -d 'value=123' $URL/foo
curl -s -X PUT -d 'value=success' $URL/bar/baz
# Fetch existing key
RESULT=$(curl -s $URL/bar/baz)
test "$RESULT" = "success" || exit 1
# Delete it
curl -s -X DELETE $URL/bar/baz
# Make sure it's deleted - GET must result in 404
RESULT=$(curl -s -i $URL/bar/baz | head -1 | tr -d '\r')
test "$RESULT" = "HTTP/1.1 404 Not Found" || exit 1
exit 0
@@ -2,32 +2,37 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>WebSocket Test</title> <title>RESTful API demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css"> <style type="text/css">
body {
background-color: #cde; margin: 0;
padding: 0; font: 14px Helvetica, Arial, sans-serif;
}
* { outline: none; } * { outline: none; }
body {
background-color: #789; margin: 0;
padding: 0; font: 16px/1.4 Helvetica, Arial, sans-serif;
font: 16px/1.4 Helvetica, Arial, sans-serif;
}
div.content { div.content {
width: 800px; margin: 2em auto; padding: 20px 50px; width: 800px; margin: 2em auto; padding: 20px 50px;
background-color: #fff; border-radius: 1em; background-color: #fff; border-radius: 1em;
} }
label { display: inline-block; min-width: 7em; } label { display: inline-block; min-width: 7em; }
input { border: 1px solid #ccc; padding: 0.4em; margin: 0 0 10px 0; } input { border: 1px solid #ccc; padding: 0.2em; }
a:link, a:visited { color: #69c; text-decoration: none; } a:link, a:visited { color: #69c; text-decoration: none; }
@media (max-width: 700px) { @media (max-width: 700px) {
body { background-color: #fff; } body { background-color: #fff; }
div.content { div.content { width: auto; margin: 0 auto; padding: 1em; }
width: auto; margin: 0 auto; border-radius: 0; padding: 1em;
}
} }
</style> </style>
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script language="javascript" type="text/javascript">
jQuery(function() {
});
</script>
</head>
<body> <body>
<div class="content"> <div class="content">
<h1>Mongoose Cookie Base Authentication</h1> <h1>API server</h1>
<p>This is an index page. Authentication succeeded.</p> </div>
</body> </body>
</html> </html>
+2
View File
@@ -0,0 +1,2 @@
# This "makefile" is only intended to prevent errors during main makefile execution
all:
+46
View File
@@ -0,0 +1,46 @@
# Arduino Restful Client
This example demonstrates how to use [Mongoose](https://www.cesanta.com/mongoose) to send HTTP commands from Arduino.
Example sends free memory size and current board uptime, but it can be modified to send any user-specific data.
At the moment this example supports [Arduino Mega 2560](http://arduino.cc/en/Main/ArduinoBoardMega2560) board (and compatible) with either W5100-based
network shield (like [Arduino Ethernet Shield](http://arduino.cc/en/Main/ArduinoEthernetShield)) or [CC3000](http://www.ti.com/product/cc3000)-based WIFI Shield.
## Build and run instructions:
###To run with Arduino Ethernet (W5100) shield:
1. Add (Sketch->Add file...) the following files to sketch:
- /mongoose/mongoose.h
- /mongoose/mongoose.c
- /mongoose/platforms/arduino_ethernet_W5100/avrsupport.h
- /mongoose/platforms/arduino_ethernet_W5100/avrsupport.cpp
3. Buils and start (in console) /Users/alex/Projects/mongoose/examples/restful_server example
4. Make `board_ip` and `board_mac` variables suitable for your network and board
5. Change IP address in `s_target_address` variable to IP address of host running restful_server
6. Uncomment line `#include <Ethernet.h>`
7. Compile and flash sketch
8. restful_server will start to show current uptime and free memory size (with 5 seconds interval)
###To run with Adafruit WiFi (CC3000) shield:
1. Add (Sketch->Add files...) the following files to sketch:
- /mongoose/mongoose.h
- /mongoose/mongoose.c
- /mongoose/platforms/arduino_ethernet_W5100/avrsupport.h
- /mongoose/platforms/arduino_ethernet_W5100/avrsupport.cpp
2. Import Adafruit CC3000 library for mongoose
(select Sketch->Import Library...->Add library... and point
/mongoose/platforms/arduino_wifi_CC3000/adafruit_CC3000_lib_mongoose folder)
3. Buils and start (in console) /Users/alex/Projects/mongoose/examples/restful_server example
4. Make the following variables suitable for your network
- `board_ip`
- `subnet_mask`
- `gateway`
- `dns`
- `wlan_ssid`
- `wlan_pwd`
- `wlan_security`
5. Change IP address in `s_target_address` variable to IP address of host running restful_server
6. Uncomment line `#include <Adafruit_CC3000.h>`
8. Compile and flash sketch
9. restful_server will start to show current uptime and free memory size (with 5 seconds interval)
@@ -0,0 +1,141 @@
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*
* Build and run instructions:
* To run with Arduino Ethernet (W5100) shield:
* -----------------------------------------------------------
* 1. Add (Sketch->Add file...) the following files to sketch:
* - /mongoose/mongoose.h
* - /mongoose/mongoose.c
* - /mongoose/platforms/arduino_ethernet_W5100/avrsupport.h
* - /mongoose/platforms/arduino_ethernet_W5100/avrsupport.cpp
* 2. Buils and run in console /Users/alex/Projects/mongoose/examples/restful_server example
* 3. Make board_ip and board_mac variables suitable for your network and board
* 4. Uncomment line #include <Ethernet.h>
* 5. Change IP address in s_target_address variable to IP address of host running restful_server
* 6. Compile & flash sketch
* 7. restful_server server will start to show current uptime and free memory size (with 5 second interval)
*
* To run with Adafruit WiFi (CC3000) shield:
* -----------------------------------------------------------
* 1. Add (Sketch->Add files...) the following files to sketch:
* - /mongoose/mongoose.h
* - /mongoose/mongoose.c
* - /mongoose/platforms/arduino_ethernet_W5100/avrsupport.h
* - /mongoose/platforms/arduino_ethernet_W5100/avrsupport.cpp
* 2. Import Adafruit CC3000 library for mongoose (select Sketch->Import Library...->Add library... and point
* /mongoose/platforms/arduino_wifi_CC3000/adafruit_CC3000_lib_mongoose folder
* 3. Buils and run in console /Users/alex/Projects/mongoose/examples/restful_server example
* 4. Make the following variables suitable for your network
* - board_ip
* - subnet_mask
* - gateway
* - dns
* - wlan_ssid
* - wlan_pwd
* - wlan_security
* 5. Uncomment line #include <Adafruit_CC3000.h>
* 6. Compile & flash sketch
* 7. restful_server server will start to show current uptime and free memory size (with 5 second interval) *
*
*/
//#include <Ethernet.h>
//#include <Adafruit_CC3000.h>
#include <SPI.h>
#include "mongoose.h"
// CHANGE THESE VARIABLES
// NB: Devices with the same address must not end up on the same network.
// Use MAC address provided by device manufacturer (e.g. on a sticker).
// If there isn't one, use a random one from the locally administered range.
// See http://en.wikipedia.org/wiki/MAC_address for details.
static uint8_t board_mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
static uint8_t board_ip[] = {192, 168, 10, 177};
#ifdef WIFI_CC3000
static uint8_t subnet_mask[] = {255, 255, 255, 0};
static uint8_t gateway[] = {192, 168, 10, 254};
static uint8_t dns_ip[] = {192, 168, 10, 254};
static const char *wlan_ssid = "mynetwork";
static const char *wlan_pwd = "mypassword";
static int wlan_security = WLAN_SEC_WPA2;
#endif
static const char *s_target_address = "192.168.10.3:8000";
/////////////////////////////////////////////
static const char *s_request = "/printcontent";
static uint32_t IP2U32(uint8_t* iparr) {
return ((uint32_t)iparr[0] << 24) | ((uint32_t)iparr[1] << 16) | (iparr[2] << 8) | (iparr[3]);
}
static int get_data_to_send(char* buf, int buf_size) {
// Adding data to send
// It could be any sensor data, now just put uptime & free memory size here
return snprintf(buf, buf_size, "Uptime: %lus Free memory: %db",
millis()/1000, get_freememsize());
}
static void rfc_ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
int connect_status;
switch (ev) {
case NS_CONNECT:
connect_status = * (int *) ev_data;
if (connect_status == 0) {
char buf[100];
int len = get_data_to_send(buf, sizeof(buf));
mg_printf(nc, "POST %s HTTP/1.0\r\nHost: %s\r\nContent-Lenght: %d"
"\r\n\r\n%s", s_request, s_target_address, len, buf);
nc->flags |= MG_F_SEND_AND_CLOSE;
} else {
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
}
break;
default:
break;
}
}
static struct mg_mgr mgr;
static struct mg_connection *nc;
void setup()
{
Serial.begin(9600);
Serial.println("Initialization...");
#if defined(ETHERNET_W5100)
avr_netinit(board_mac, board_ip);
#elif defined(WIFI_CC3000)
if (avr_netinit(wlan_ssid, wlan_pwd, wlan_security, IP2U32(board_ip),
IP2U32(subnet_mask), IP2U32(gateway), IP2U32(dns_ip)) != 0) {
Serial.println("Initialization error, check network settings");
return;
};
#endif
mg_mgr_init(&mgr, NULL);
Serial.println("Initialization done");
}
void loop() {
nc = mg_connect(&mgr, s_target_address, rfc_ev_handler);
if (nc != NULL) {
mg_set_protocol_http_websocket(nc);
}
uint32_t time_to_finish = millis() + 5000;
while (millis() < time_to_finish) {
mg_mgr_poll(&mgr, 1000);
}
}
+2
View File
@@ -0,0 +1,2 @@
# This "makefile" is only intended to prevent errors during main makefile execution
all:
+45
View File
@@ -0,0 +1,45 @@
# Arduino Restful Server
This example demonstrates how to use [Mongoose](https://www.cesanta.com/mongoose) to control Arduino
using HTTP requests.
Example just blinks by LED when Mongoose receives HTTP command, but it can be modified to execute any user-specific code.
At the moment this example supports [Arduino Mega 2560](http://arduino.cc/en/Main/ArduinoBoardMega2560) board (and compatible) with either W5100-based
network shield (like [Arduino Ethernet Shield](http://arduino.cc/en/Main/ArduinoEthernetShield)) or [CC3000](http://www.ti.com/product/cc3000)-based WIFI Shield.
## Build and run instructions:
###To run with Arduino Ethernet (W5100) shield:
1. Add (Sketch->Add file...) the following files to sketch:
- /mongoose/mongoose.h
- /mongoose/mongoose.c
- /mongoose/platforms/arduino_ethernet_W5100/avrsupport.h
- /mongoose/platforms/arduino_ethernet_W5100/avrsupport.cpp
2. Make `board_ip` and `board_mac` variables suitable for your network and board
3. Uncomment line `#include <Ethernet.h>`
4. Compile and flash sketch
5. Run `curl http://<board_ip/blink`
LED attached to PIN 13 will blink and board free memory size and board uptime will be displayed.
###To run with Adafruit WiFi (CC3000) shield:
1. Add (Sketch->Add files...) the following files to sketch:
- /mongoose/mongoose.h
- /mongoose/mongoose.c
- /mongoose/platforms/arduino_ethernet_W5100/avrsupport.h
- /mongoose/platforms/arduino_ethernet_W5100/avrsupport.cpp
2. Import Adafruit CC3000 library for mongoose
(select Sketch->Import Library...->Add library... and point
/mongoose/platforms/arduino_wifi_CC3000/adafruit_CC3000_lib_mongoose folder)
3. Make the following variables suitable for your network
- `board_ip`
- `subnet_mask`
- `gateway`
- `dns`
- `wlan_ssid`
- `wlan_pwd`
- `wlan_security`
5. Uncomment line `#include <Adafruit_CC3000.h>`
4. Compile and flash sketch
5. Run curl `http://<board_ip/blink`
LED attached to PIN 13 will blink and board free memory size and board uptime will be displayed.
@@ -0,0 +1,133 @@
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*
* Build and run instructions:
* To run with Arduino Ethernet (W5100) shield:
* -----------------------------------------------------------
* 1. Add (Sketch->Add file...) the following files to sketch:
* - /mongoose/mongoose.h
* - /mongoose/mongoose.c
* - /mongoose/platforms/arduino_ethernet_W5100/avrsupport.h
* - /mongoose/platforms/arduino_ethernet_W5100/avrsupport.cpp
* 2. Make board_ip and board_mac variables suitable for your network and board
* 3. Uncomment line #include <Ethernet.h>
* 4. Compile & flash sketch
* 5. Run curl http://<board_ip/blink
* LED attached to PIN 13 will blink and board free memory size and uptime will responsed
*
* To run with Adafruit WiFi (CC3000) shield:
* -----------------------------------------------------------
* 1. Add (Sketch->Add files...) the following files to sketch:
* - /mongoose/mongoose.h
* - /mongoose/mongoose.c
* - /mongoose/platforms/arduino_ethernet_W5100/avrsupport.h
* - /mongoose/platforms/arduino_ethernet_W5100/avrsupport.cpp
* 2. Import Adafruit CC3000 library for mongoose (select Sketch->Import Library...->Add library... and point
* /mongoose/platforms/arduino_wifi_CC3000/adafruit_CC3000_lib_mongoose folder
* 3. Make the following variables suitable for your network
* - board_ip
* - subnet_mask
* - gateway
* - dns
* - wlan_ssid
* - wlan_pwd
* - wlan_security
* 5. Uncomment line #include <Adafruit_CC3000.h>
* 4. Compile & flash sketch
* 5. Run curl http://<board_ip/blink
* LED attached to PIN 13 will blink and board free memory size and uptime will responsed
*
*/
//#include <Ethernet.h>
//#include <Adafruit_CC3000.h>
#include <SPI.h>
#include "mongoose.h"
// CHANGE THESE VARIABLES
// NB: Devices with the same address must not end up on the same network.
// Use MAC address provided by device manufacturer (e.g. on a sticker).
// If there isn't one, use a random one from the locally administered range.
// See http://en.wikipedia.org/wiki/MAC_address for details.
static uint8_t board_mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
static uint8_t board_ip[] = {192, 168, 10, 8};
#ifdef WIFI_CC3000
static uint8_t subnet_mask[] = {255, 255, 255, 0};
static uint8_t gateway[] = {192, 168, 10, 254};
static uint8_t dns_ip[] = {192, 168, 10, 254};
static const char *wlan_ssid = "mynetwork";
static const char *wlan_pwd = "mypassword";
static int wlan_security = WLAN_SEC_WPA2;
#endif
///////////////////////////////////////////////
static const char *s_http_port = "60000";
static uint32_t IP2U32(uint8_t* iparr) {
return ((uint32_t)iparr[0] << 24) | ((uint32_t)iparr[1] << 16) | (iparr[2] << 8) | (iparr[3]);
}
static void rfs_ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct http_message *hm = (struct http_message *) ev_data;
char buf[100];
int clen;
switch (ev) {
case NS_HTTP_REQUEST:
if (mg_vcmp(&hm->uri, "/blink") == 0) {
blink(1, 500);
}
clen = snprintf(buf, sizeof(buf),
"Free memory size: %d Uptime: %d",
(int)get_freememsize(), (int)time(NULL));
mg_printf_http_chunk(nc, "HTTP/1.1 200 OK\r\n"
"Content-Length: %d\r\n"
"Transfer-Encoding: chunked\r\n\r\n"
"%s",
clen, buf);
mg_send_http_chunk(nc, "", 0);
break;
case NS_SEND:
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
break;
default:
break;
}
}
static struct mg_connection *nc;
static struct mg_mgr mgr;
void setup() {
Serial.begin(9600);
Serial.println("Initialization...");
#if defined(ETHERNET_W5100)
avr_netinit(board_mac, board_ip);
#elif defined(WIFI_CC3000)
if (avr_netinit(wlan_ssid, wlan_pwd, wlan_security, IP2U32(board_ip),
IP2U32(subnet_mask), IP2U32(gateway), IP2U32(dns_ip)) != 0) {
Serial.println("Initialization error, check network settings");
return;
};
#endif
mg_mgr_init(&mgr, NULL);
nc = mg_bind(&mgr, s_http_port, rfs_ev_handler);
mg_set_protocol_http_websocket(nc);
Serial.println("Initialization done");
}
void loop() {
mg_mgr_poll(&mgr, 1000);
}
+1 -11
View File
@@ -1,12 +1,2 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = big_upload PROG = big_upload
CFLAGS = -W -Wall -pthread -I../.. -g -O0 $(CFLAGS_EXTRA) include ../examples.mk
SOURCES = $(PROG).c ../../mongoose.c
$(PROG): $(SOURCES)
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
+126 -57
View File
@@ -1,84 +1,153 @@
// Copyright (c) 2015 Cesanta Software Limited
// All rights reserved
//
// This example demonstrates how to handle very large requests without keeping
// them in memory.
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "mongoose.h" #include "mongoose.h"
static int handle_request(struct mg_connection *conn) { static const char *s_http_port = "8000";
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 struct file_writer_data {
// data here -- for example, parse it and extract uploaded files. FILE *fp;
// As an example, we just echo the whole POST buffer back to the client. size_t bytes_left;
rewind(fp); };
mg_send_file_data(conn, fileno(fp));
return MG_MORE; // Tell Mongoose reply is not completed yet static void handle_request(struct mg_connection *nc) {
} else { // This handler gets called only when we don't reset proto_handler in
mg_printf_data(conn, "%s", "Had no data to write..."); // handle_recv, so we just return a page unconditionally.
return MG_TRUE; // Tell Mongoose we're done with this request mg_printf(nc, "%s",
} "HTTP/1.1 200 OK\r\n"
} else { "Content-Type: text/html\r\n"
mg_printf_data(conn, "%s", "Connection: close\r\n"
"<html><body>Upload example." "\r\n"
"<form method=\"POST\" action=\"/upload\" " "<html><body>Upload example."
" enctype=\"multipart/form-data\">" "<form method=\"POST\" action=\"/upload\" "
"<input type=\"file\" name=\"file\" /> <br/>" " enctype=\"multipart/form-data\">"
"<input type=\"submit\" value=\"Upload\" />" "<input type=\"file\" name=\"file\" /> <br/>"
"</form></body></html>"); "<input type=\"submit\" value=\"Upload\" />"
return MG_TRUE; // Tell mongoose to close this connection "</form></body></html>");
} nc->flags |= MG_F_SEND_AND_CLOSE;
} }
// Mongoose sends MG_RECV for every received POST chunk. static void handle_recv(struct mg_connection *nc) {
// When last POST chunk is received, Mongoose sends MG_REQUEST, then MG_CLOSE. struct file_writer_data *data = (struct file_writer_data *) nc->user_data;
static int handle_recv(struct mg_connection *conn) {
FILE *fp = (FILE *) conn->connection_param;
// Open temporary file where we going to write data if (data == NULL) {
if (fp == NULL && ((conn->connection_param = fp = tmpfile())) == NULL) { // This is a new connection, try to parse HTTP request.
return -1; // Close connection on error struct http_message hm;
int req_len = mg_parse_http(nc->recv_mbuf.buf, nc->recv_mbuf.len, &hm,
1 /* is_req */);
if (req_len < 0 ||
(req_len == 0 && nc->recv_mbuf.len >= MG_MAX_HTTP_REQUEST_SIZE)) {
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
} else if (req_len == 0) {
// Request is not complete yet, do nothing.
} else if (mg_vcasecmp(&hm.method, "POST") == 0 &&
mg_vcmp(&hm.uri, "/upload") == 0) {
// This is the request that we don't want to buffer in memory.
if (hm.body.len == (size_t) ~0 || hm.body.len == 0) {
mg_printf(nc, "%s",
"HTTP/1.1 411 Content-Length required\r\n"
"Content-Length: 0\r\n\r\n");
nc->flags |= MG_F_SEND_AND_CLOSE;
return;
}
// Reset proto_handler so Mongoose's http_handler() won't get confused
// once we start removing data from the buffer.
nc->proto_handler = NULL;
// Headers will be inaccessible later, so put everything we need into
// user_data.
data = calloc(1, sizeof(struct file_writer_data));
data->bytes_left = hm.body.len;
data->fp = tmpfile();
if (data->fp == NULL) {
mg_printf(nc, "%s",
"HTTP/1.1 500 Failed to open a file\r\n"
"Content-Length: 0\r\n\r\n");
nc->flags |= MG_F_SEND_AND_CLOSE;
return;
}
nc->user_data = (void *) data;
// Remove the headers from the buffer.
mbuf_remove(&nc->recv_mbuf, hm.body.p - nc->recv_mbuf.buf);
// Invoke itself again to write the piece of the body that is already in
// the buffer.
handle_recv(nc);
}
} else { // data != NULL
size_t to_write = data->bytes_left, written = 0;
if (nc->recv_mbuf.len < to_write) to_write = nc->recv_mbuf.len;
written = fwrite(nc->recv_mbuf.buf, 1, to_write, data->fp);
mbuf_remove(&nc->recv_mbuf, written);
data->bytes_left -= written;
if (data->bytes_left <= 0) {
// Request is complete, do something meaningful here.
mg_printf(nc,
"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(data->fp));
nc->flags |= MG_F_SEND_AND_CLOSE;
// handle_close will free the resources.
}
} }
// 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 // Make sure we free all allocated resources
static int handle_close(struct mg_connection *conn) { static void handle_close(struct mg_connection *nc) {
if (conn->connection_param != NULL) { struct file_writer_data *data = (struct file_writer_data *) nc->user_data;
fclose((FILE *) conn->connection_param);
conn->connection_param = NULL; if (data != NULL) {
fclose(data->fp);
free(data);
nc->user_data = NULL;
} }
return MG_TRUE;
} }
static int ev_handler(struct mg_connection *conn, enum mg_event ev) { static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
(void) ev_data;
switch (ev) { switch (ev) {
case MG_AUTH: return MG_TRUE; case MG_EV_HTTP_REQUEST:
case MG_REQUEST: return handle_request(conn); // Invoked when the full HTTP request is in the buffer (including body).
case MG_RECV: return handle_recv(conn); handle_request(nc);
case MG_CLOSE: return handle_close(conn); break;
default: return MG_FALSE; case MG_EV_RECV:
// Invoked every time new data arrives.
handle_recv(nc);
break;
case MG_EV_CLOSE:
handle_close(nc);
break;
} }
} }
int main(void) { int main(void) {
struct mg_server *server = mg_create_server(NULL, ev_handler); struct mg_mgr mgr;
mg_set_option(server, "listening_port", "8080"); struct mg_connection *nc;
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
mg_mgr_init(&mgr, NULL);
nc = mg_bind(&mgr, s_http_port, ev_handler);
// Set up HTTP server parameters
mg_set_protocol_http_websocket(nc);
printf("Starting web server on port %s\n", s_http_port);
for (;;) { for (;;) {
mg_poll_server(server, 1000); mg_mgr_poll(&mgr, 1000);
} }
mg_mgr_free(&mgr);
mg_destroy_server(&server);
return 0; return 0;
} }
+4
View File
@@ -0,0 +1,4 @@
PROG = captive_dns_server
MODULE_CFLAGS=-DMG_ENABLE_DNS_SERVER
include ../rules.mk
@@ -0,0 +1,83 @@
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
/*
* Try it out with:
* $ dig -t A www.google.com -4 @localhost -p 5533
*/
#include "../../mongoose.h"
#include <stdio.h>
static int s_exit_flag = 0;
static in_addr_t s_our_ip_addr;
static const char *s_listening_addr = "udp://:5533";
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct mg_dns_message *msg;
struct mg_dns_resource_record *rr;
struct mg_dns_reply reply;
int i;
switch (ev) {
case MG_DNS_MESSAGE:
msg = (struct mg_dns_message *) ev_data;
reply = mg_dns_create_reply(&nc->send_mbuf, msg);
for (i = 0; i < msg->num_questions; i++) {
rr = &msg->questions[i];
if (rr->rtype == MG_DNS_A_RECORD) {
mg_dns_reply_record(&reply, rr, NULL, rr->rtype, 3600,
&s_our_ip_addr, 4);
}
}
/*
* We don't set the error flag even if there were no answers
* maching the MG_DNS_A_RECORD query type.
* This indicates that we have (syntetic) answers for MG_DNS_A_RECORD.
* See http://goo.gl/QWvufr for a distinction between NXDOMAIN and NODATA.
*/
mg_dns_send_reply(nc, &reply);
nc->flags |= MG_F_SEND_AND_CLOSE;
break;
}
}
int main(int argc, char *argv[]) {
struct mg_mgr mgr;
struct mg_connection *nc;
int i;
mg_mgr_init(&mgr, NULL);
s_our_ip_addr = inet_addr("127.0.0.1");
/* Parse command line arguments */
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-D") == 0) {
mgr.hexdump_file = argv[++i];
} else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) {
s_listening_addr = argv[++i];
} else {
s_our_ip_addr = inet_addr(argv[i]);
}
}
fprintf(stderr, "Listening on '%s'\n", s_listening_addr);
if ((nc = mg_bind(&mgr, s_listening_addr, ev_handler)) == NULL) {
fprintf(stderr, "cannot bind to socket\n");
exit(1);
}
mg_set_protocol_dns(nc);
while (s_exit_flag == 0) {
mg_mgr_poll(&mgr, 1000);
}
mg_mgr_free(&mgr);
return 0;
}
+14
View File
@@ -0,0 +1,14 @@
PROG = coap_client
SOURCES = $(PROG).c ../../mongoose.c
CFLAGS = -W -Wall -I../.. $(CFLAGS_EXTRA) -DMG_ENABLE_COAP
all: $(PROG)
$(PROG): $(SOURCES)
$(CC) $(SOURCES) -o $@ $(CFLAGS)
$(PROG).exe: $(SOURCES)
cl $(SOURCES) /I../.. /MD /Fe$@
clean:
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
+71
View File
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*
* This program sends CoAP CON-message to server (coap.me by default)
* and waits for answer.
*/
#include "mongoose.h"
static int s_time_to_exit = 0;
static char *s_default_address = "udp://coap.me:5683";
static void coap_handler(struct mg_connection *nc, int ev, void *p) {
switch (ev) {
case MG_EV_CONNECT: {
struct mg_coap_message cm;
uint32_t res;
memset(&cm, 0, sizeof(cm));
cm.msg_id = 1;
cm.msg_type = MG_COAP_MSG_CON;
printf("Sending CON...\n");
res = mg_coap_send_message(nc, &cm);
if (res == 0) {
printf("Sent CON with msg_id = %d\n", cm.msg_id);
} else {
printf("Error: %d\n", res);
s_time_to_exit = 1;
}
break;
}
case MG_EV_COAP_ACK:
case MG_EV_COAP_RST: {
struct mg_coap_message *cm = (struct mg_coap_message *) p;
printf("ACK/RST for message with msg_id = %d received\n", cm->msg_id);
s_time_to_exit = 1;
break;
}
}
}
int main(int argc, char *argv[]) {
struct mg_mgr mgr;
struct mg_connection *nc;
char *address = s_default_address;
if (argc > 1) {
address = argv[1];
}
printf("Using %s as CoAP server\n", address);
mg_mgr_init(&mgr, 0);
nc = mg_connect(&mgr, address, coap_handler);
if (nc == NULL) {
printf("Unable to connect to %s\n", address);
return -1;
}
mg_set_protocol_coap(nc);
while (!s_time_to_exit) {
mg_mgr_poll(&mgr, 1000000);
}
mg_mgr_free(&mgr);
return 0;
}
+14
View File
@@ -0,0 +1,14 @@
PROG = coap_server
SOURCES = $(PROG).c ../../mongoose.c
CFLAGS = -W -Wall -I../.. $(CFLAGS_EXTRA) -DMG_ENABLE_COAP
all: $(PROG)
$(PROG): $(SOURCES)
$(CC) $(SOURCES) -o $@ $(CFLAGS)
$(PROG).exe: $(SOURCES)
cl $(SOURCES) /I../.. /MD /Fe$@
clean:
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
+72
View File
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2015 Cesanta Software Limited
* All rights reserved
*
* This program listens on 5683 for CoAP messages,
* sends ACK is nessesary and dump everything received.
* It is possible to use ../coap_client to send message.
*/
#include "mongoose.h"
static char *s_default_address = "udp://:5683";
static int s_sig_received = 0;
static void signal_handler(int sig_num) {
signal(sig_num, signal_handler);
s_sig_received = sig_num;
}
static void coap_handler(struct mg_connection *nc, int ev, void *p) {
switch (ev) {
case MG_EV_COAP_CON: {
uint32_t res;
struct mg_coap_message *cm = (struct mg_coap_message *) p;
printf("CON with msg_id = %d received\n", cm->msg_id);
res = mg_coap_send_ack(nc, cm->msg_id);
if (res == 0) {
printf("Successfully sent ACK for message with msg_id = %d\n",
cm->msg_id);
} else {
printf("Error: %d\n", res);
}
break;
}
case MG_EV_COAP_NOC:
case MG_EV_COAP_ACK:
case MG_EV_COAP_RST: {
struct mg_coap_message *cm = (struct mg_coap_message *) p;
printf("ACK/RST/NOC with msg_id = %d received\n", cm->msg_id);
break;
}
}
}
int main() {
struct mg_mgr mgr;
struct mg_connection *nc;
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
mg_mgr_init(&mgr, 0);
nc = mg_bind(&mgr, s_default_address, coap_handler);
if (nc == NULL) {
printf("Unable to start listener at %s\n", s_default_address);
return -1;
}
printf("Listening for CoAP messages at %s\n", s_default_address);
mg_set_protocol_coap(nc);
while (!s_sig_received) {
mg_mgr_poll(&mgr, 1000000);
}
printf("Exiting on signal %d\n", s_sig_received);
mg_mgr_free(&mgr);
return 0;
}
+2
View File
@@ -0,0 +1,2 @@
PROG = server
include ../examples.mk
+43
View File
@@ -0,0 +1,43 @@
// Copyright (c) 2015 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
static const char *s_http_port = "8000";
static struct mg_serve_http_opts s_http_server_opts;
static void ev_handler(struct mg_connection *nc, int ev, void *p) {
if (ev == MG_EV_HTTP_REQUEST) {
mg_serve_http(nc, p, s_http_server_opts); // Serves static content
}
}
int main(void) {
struct mg_mgr mgr;
struct mg_connection *nc;
cs_stat_t st;
mg_mgr_init(&mgr, NULL);
nc = mg_bind(&mgr, s_http_port, ev_handler);
if (nc == NULL) {
fprintf(stderr, "Cannot bind to %s\n", s_http_port);
exit(1);
}
// Set up HTTP server parameters
mg_set_protocol_http_websocket(nc);
s_http_server_opts.document_root = "web_root"; // Set up web root directory
if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
exit(1);
}
printf("Starting web server on port %s\n", s_http_port);
for (;;) {
mg_mgr_poll(&mgr, 1000);
}
mg_mgr_free(&mgr);
return 0;
}
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Connected device demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="content">
<img src="logo.png" style="float:right; height: 50px; border-radius: 3px;">
<h1>Connected device demo - static content only</h1>
<p>
This page shows initial steps on how to make a device connected.
Mongoose serves static content from the <code>web_root</code>
directory. Images, CSS, HTML, JavaScript files could be dropped into the
<code>web_root</code> directory - and they'll be served happily
by Mongoose.
</p>
<p>
Below is a placeholder for the
settings section, and a real-time dashboard, which will be
implemented in the following examples.
Both settings section, and a dashboard section, could be organised
differently on this UI - for example, in different tabs. But, to
make the demonstration clearer, we show everything on one page.
</p>
<h1>Settings section</h1>
<p>Nothing here yet</p>
<h1>Dashboard section</h1>
<p>Nothing here yet</p>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

@@ -0,0 +1,31 @@
* {
outline: none;
}
body {
background-color: #36c;
margin: 0;
padding: 0;
font: 16px/1.4 Helvetica, Arial, sans-serif;
}
div.content {
width: 800px;
margin: 1em auto;
padding: 2em;
background-color: #fff;
border-radius: 1em;
}
code {
background: #f0f0f0;
padding: 0 0.3em;
color: #396;
border-radius: 0.3em;
}
a:link, a:visited {
color: #69c;
text-decoration: none;
}
+2
View File
@@ -0,0 +1,2 @@
PROG = server
include ../examples.mk
+81
View File
@@ -0,0 +1,81 @@
// Copyright (c) 2015 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
struct device_settings {
char setting1[100];
char setting2[100];
};
static const char *s_http_port = "8000";
static struct mg_serve_http_opts s_http_server_opts;
static struct device_settings s_settings = { "value1", "value2" };
static void handle_save(struct mg_connection *nc, struct http_message *hm) {
// Get form variables and store settings values
mg_get_http_var(&hm->body, "setting1", s_settings.setting1,
sizeof(s_settings.setting1));
mg_get_http_var(&hm->body, "setting2", s_settings.setting2,
sizeof(s_settings.setting2));
// Send response
mg_printf(nc, "%s", "HTTP/1.1 302 OK\r\nLocation: /\r\n\r\n");
}
static void handle_ssi_call(struct mg_connection *nc, const char *param) {
if (strcmp(param, "setting1") == 0) {
mg_printf_html_escape(nc, "%s", s_settings.setting1);
} else if (strcmp(param, "setting2") == 0) {
mg_printf_html_escape(nc, "%s", s_settings.setting2);
}
}
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct http_message *hm = (struct http_message *) ev_data;
switch (ev) {
case MG_EV_HTTP_REQUEST:
if (mg_vcmp(&hm->uri, "/save") == 0) {
handle_save(nc, hm);
} else {
mg_serve_http(nc, hm, s_http_server_opts); // Serve static content
}
break;
case MG_EV_SSI_CALL:
handle_ssi_call(nc, ev_data);
break;
default:
break;
}
}
int main(void) {
struct mg_mgr mgr;
struct mg_connection *nc;
cs_stat_t st;
mg_mgr_init(&mgr, NULL);
nc = mg_bind(&mgr, s_http_port, ev_handler);
if (nc == NULL) {
fprintf(stderr, "Cannot bind to %s\n", s_http_port);
exit(1);
}
// Set up HTTP server parameters
mg_set_protocol_http_websocket(nc);
s_http_server_opts.document_root = "web_root"; // Set up web root directory
if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
exit(1);
}
printf("Starting web server on port %s\n", s_http_port);
for (;;) {
mg_mgr_poll(&mgr, 1000);
}
mg_mgr_free(&mgr);
return 0;
}
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Connected device demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="content">
<img src="logo.png" style="float:right; height: 50px; border-radius: 3px;">
<h1>Connected device demo - settings section</h1>
<p>
This page is one of the series of examples that show how to make
devices connected with Mongoose. Here, we create a settings section
that expose some internal device variables, and allow user to change
them. They could be some stored in a database, in a file, or in
firmware RAM. In this example, we store them in RAM, and use two
variables, one integer and one string. We use SSI
<code>&lt;!--#call parameter_name --&gt;</code> directive to fetch
variable values from Mongoose.
</p>
<h1>Settings section</h1>
<form method="POST" action="/save">
<fieldset>
<legend>Device settings</legend>
<label>Setting 1:</label> <input type="text"
name="setting1" value="<!--#call setting1 -->" >
<label>Setting 2:</label> <input type="text"
name="setting2" value="<!--#call setting2 -->" >
<div>
<button type="submit">Save Settings</button>
</div>
</fieldset>
</form>
<h1>Dashboard section</h1>
<p>Nothing here yet</p>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

@@ -0,0 +1,67 @@
* {
outline: none;
}
body {
background: #36c;
color: #555;
margin: 0;
padding: 0;
font: 16px/1.4 Helvetica, Arial, sans-serif;
}
div.content {
width: 800px;
margin: 1em auto;
padding: 2em;
background-color: #fff;
border-radius: 1em;
}
fieldset {
border: 1px solid #ccc;
padding: 1em;
}
fieldset legend {
font-style: italic;
color: silver;
}
fieldset label {
text-align: right;
padding: 0 1em;
}
input[type=text] {
font-size: 100%;
padding: 0.2em;
border: 1px solid #ccc;
}
button {
color: #666;
background: #eee;
border: 1px solid #ccc;
padding: 0.2em 2em;
font-size: 100%;
cursor: pointer;
margin-top: 1em;
}
button:hover {
background: #ccc;
}
code {
background: #f0f0f0;
padding: 0 0.3em;
color: #396;
border-radius: 0.3em;
}
a:link, a:visited {
color: #69c;
text-decoration: none;
}
+2
View File
@@ -0,0 +1,2 @@
PROG = server
include ../examples.mk
+98
View File
@@ -0,0 +1,98 @@
// Copyright (c) 2015 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
struct device_settings {
char setting1[100];
char setting2[100];
};
static const char *s_http_port = "8000";
static struct mg_serve_http_opts s_http_server_opts;
static struct device_settings s_settings = { "value1", "value2" };
static void handle_save(struct mg_connection *nc, struct http_message *hm) {
// Get form variables and store settings values
mg_get_http_var(&hm->body, "setting1", s_settings.setting1,
sizeof(s_settings.setting1));
mg_get_http_var(&hm->body, "setting2", s_settings.setting2,
sizeof(s_settings.setting2));
// Send response
mg_printf(nc, "%s", "HTTP/1.1 302 OK\r\nLocation: /\r\n\r\n");
}
static void handle_get_cpu_usage(struct mg_connection *nc) {
// Generate random value, as an example of changing CPU usage
// Getting real CPU usage depends on the OS.
int cpu_usage = (double) rand() / RAND_MAX * 100.0;
// Use chunked encoding in order to avoid calculating Content-Length
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
// Output JSON object which holds CPU usage data
mg_printf_http_chunk(nc, "{ \"result\": %d }", cpu_usage);
// Send empty chunk, the end of response
mg_send_http_chunk(nc, "", 0);
}
static void handle_ssi_call(struct mg_connection *nc, const char *param) {
if (strcmp(param, "setting1") == 0) {
mg_printf_html_escape(nc, "%s", s_settings.setting1);
} else if (strcmp(param, "setting2") == 0) {
mg_printf_html_escape(nc, "%s", s_settings.setting2);
}
}
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct http_message *hm = (struct http_message *) ev_data;
switch (ev) {
case MG_EV_HTTP_REQUEST:
if (mg_vcmp(&hm->uri, "/save") == 0) {
handle_save(nc, hm);
} else if (mg_vcmp(&hm->uri, "/get_cpu_usage") == 0) {
handle_get_cpu_usage(nc);
} else {
mg_serve_http(nc, hm, s_http_server_opts); // Serve static content
}
break;
case MG_EV_SSI_CALL:
handle_ssi_call(nc, ev_data);
break;
default:
break;
}
}
int main(void) {
struct mg_mgr mgr;
struct mg_connection *nc;
cs_stat_t st;
mg_mgr_init(&mgr, NULL);
nc = mg_bind(&mgr, s_http_port, ev_handler);
if (nc == NULL) {
fprintf(stderr, "Cannot bind to %s\n", s_http_port);
exit(1);
}
// Set up HTTP server parameters
mg_set_protocol_http_websocket(nc);
s_http_server_opts.document_root = "web_root"; // Set up web root directory
if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
exit(1);
}
printf("Starting web server on port %s\n", s_http_port);
for (;;) {
mg_mgr_poll(&mgr, 1000);
}
mg_mgr_free(&mgr);
return 0;
}
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Connected device demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript" src="jquery-1.11.3.min.js"></script>
<script type="text/javascript" src="main.js"></script>
</head>
<body>
<div class="content">
<img src="logo.png" style="float:right; height: 50px; border-radius: 3px;">
<h1>Connected device demo - RESTful call</h1>
<p>
This page is one of the series of examples that show how to make
devices connected with Mongoose. Here we add a RESTful call
to the Mongoose to show CPU usage stats on a dashboard.
RESTful call is done using Jquery's <code>ajax</code> call.
</p>
<h1>Settings section</h1>
<form method="POST" action="/save">
<fieldset>
<legend>Device settings</legend>
<label>Setting 1:</label> <input type="text"
name="setting1" value="<!--#call setting1 -->" >
<label>Setting 2:</label> <input type="text"
name="setting2" value="<!--#call setting2 -->" >
<div>
<button type="submit">Save Settings</button>
</div>
</fieldset>
</form>
<h1>Dashboard section</h1>
<label>CPU usage:</label> <span id="cpu_usage">&nbsp;</span>
</div>
</body>
</html>
@@ -0,0 +1,5 @@
/*! jQuery v1.11.3 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */
!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.3",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^Line truncated
return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var Line truncated
return new Za.prototype.init(a,b,c,d,e)}m.Tween=Za,Za.prototype={constructor:Za,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")},cur:function(){var a=Za.propHooks[this.prop];return a&&a.get?a.get(this):Za.propHooks._default.get(this)},run:function(a){var b,c=Za.propHooks[this.prop];return this.options.duration?this.pos=b=m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Za.propHooks._default.set(this),this}},Za.prototype.init.prototype=Za.prototype,Za.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Za.propHooks.scrollTop=Za.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Za.prototype.init,m.fx.step={};var $a,_a,ab=/^(?:toggle|show|hide)$/,bb=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cb=/queueHooks$/,db=[ib],eb={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bb.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bb.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fb(){return setTimeout(function(){$a=void 0}),$a=m.now()}function gb(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hb(a,b,c){for(var d,e=(eb[b]||[]).concat(eb["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ib(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fa(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fa(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ab.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fa(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hb(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jb(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kb(a,b,c){var d,e,f=0,g=db.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$a||fb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$a||fb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jb(k,j.opts.specialEasing);g>f;f++)if(d=db[f].call(j,a,k,j.opts))return d;return m.map(k,hb,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kb,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],eb[cLine truncated
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

@@ -0,0 +1,12 @@
$(document).ready(function() {
// Start 1-second timer to call RESTful endpoint
setInterval(function() {
$.ajax({
url: '/get_cpu_usage',
dataType: 'json',
success: function(json) {
$('#cpu_usage').text(json.result + '% ');
}
});
}, 1000);
});
@@ -0,0 +1,66 @@
* {
outline: none;
}
body {
background: #36c;
color: #555;
margin: 0;
padding: 0;
font: 16px/1.4 Helvetica, Arial, sans-serif;
}
div.content {
width: 800px;
margin: 1em auto;
padding: 2em;
background-color: #fff;
border-radius: 1em;
}
fieldset {
border: 1px solid #ccc;
padding: 1em;
}
fieldset legend {
font-style: italic;
color: silver;
}
fieldset label {
text-align: right;
padding: 0 1em;
}
input[type=text] {
font-size: 100%;
padding: 0.2em;
border: 1px solid #ccc;
}
button {
color: #666;
background: #eee;
border: 1px solid #ccc;
padding: 0.2em 2em;
font-size: 100%;
cursor: pointer;
margin-top: 1em;
}
button:hover {
background: #ccc;
}
code {
background: #f0f0f0;
padding: 0 0.3em;
color: #396;
border-radius: 0.3em;
}
a:link, a:visited {
color: #69c;
text-decoration: none;
}
+2
View File
@@ -0,0 +1,2 @@
PROG = server
include ../examples.mk
+115
View File
@@ -0,0 +1,115 @@
// Copyright (c) 2015 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
struct device_settings {
char setting1[100];
char setting2[100];
};
static const char *s_http_port = "8000";
static struct mg_serve_http_opts s_http_server_opts;
static struct device_settings s_settings = { "value1", "value2" };
static void handle_save(struct mg_connection *nc, struct http_message *hm) {
// Get form variables and store settings values
mg_get_http_var(&hm->body, "setting1", s_settings.setting1,
sizeof(s_settings.setting1));
mg_get_http_var(&hm->body, "setting2", s_settings.setting2,
sizeof(s_settings.setting2));
// Send response
mg_printf(nc, "%s", "HTTP/1.1 302 OK\r\nLocation: /\r\n\r\n");
}
static void handle_get_cpu_usage(struct mg_connection *nc) {
// Generate random value, as an example of changing CPU usage
// Getting real CPU usage depends on the OS.
int cpu_usage = (double) rand() / RAND_MAX * 100.0;
// Use chunked encoding in order to avoid calculating Content-Length
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
// Output JSON object which holds CPU usage data
mg_printf_http_chunk(nc, "{ \"result\": %d }", cpu_usage);
// Send empty chunk, the end of response
mg_send_http_chunk(nc, "", 0);
}
static void handle_ssi_call(struct mg_connection *nc, const char *param) {
if (strcmp(param, "setting1") == 0) {
mg_printf_html_escape(nc, "%s", s_settings.setting1);
} else if (strcmp(param, "setting2") == 0) {
mg_printf_html_escape(nc, "%s", s_settings.setting2);
}
}
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct http_message *hm = (struct http_message *) ev_data;
switch (ev) {
case MG_EV_HTTP_REQUEST:
if (mg_vcmp(&hm->uri, "/save") == 0) {
handle_save(nc, hm);
} else if (mg_vcmp(&hm->uri, "/get_cpu_usage") == 0) {
handle_get_cpu_usage(nc);
} else {
mg_serve_http(nc, hm, s_http_server_opts); // Serve static content
}
break;
case MG_EV_SSI_CALL:
handle_ssi_call(nc, ev_data);
break;
default:
break;
}
}
static void push_data_to_all_websocket_connections(struct mg_mgr *m) {
struct mg_connection *c;
int memory_usage = (double) rand() / RAND_MAX * 100.0;
for (c = mg_next(m, NULL); c != NULL; c = mg_next(m, c)) {
if (c->flags & MG_F_IS_WEBSOCKET) {
mg_printf_websocket_frame(c, WEBSOCKET_OP_TEXT, "%d", memory_usage);
}
}
}
int main(void) {
struct mg_mgr mgr;
struct mg_connection *nc;
cs_stat_t st;
mg_mgr_init(&mgr, NULL);
nc = mg_bind(&mgr, s_http_port, ev_handler);
if (nc == NULL) {
fprintf(stderr, "Cannot bind to %s\n", s_http_port);
exit(1);
}
// Set up HTTP server parameters
mg_set_protocol_http_websocket(nc);
s_http_server_opts.document_root = "web_root"; // Set up web root directory
if (mg_stat(s_http_server_opts.document_root, &st) != 0) {
fprintf(stderr, "%s", "Cannot find web_root directory, exiting\n");
exit(1);
}
printf("Starting web server on port %s\n", s_http_port);
for (;;) {
static time_t last_time;
time_t now = time(NULL);
mg_mgr_poll(&mgr, 1000);
if (now - last_time > 0) {
push_data_to_all_websocket_connections(&mgr);
last_time = now;
}
}
mg_mgr_free(&mgr);
return 0;
}
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Connected device demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript" src="jquery-1.11.3.min.js"></script>
<script src="jquery.flot.min.js"></script>
<script src="jquery.flot.time.js"></script>
<script type="text/javascript" src="main.js"></script>
</head>
<body>
<div class="content">
<img src="logo.png" style="float:right; height: 50px; border-radius: 3px;">
<h1>Connected device demo - Websocket push</h1>
<p>
This page is one of the series of examples that show how to make
devices connected with Mongoose. Here we add a real-time Websocket
server push, and update dashboard graph as data arrives.
</p>
<h1>Settings section</h1>
<form method="POST" action="/save">
<fieldset>
<legend>Device settings</legend>
<label>Setting 1:</label> <input type="text"
name="setting1" value="<!--#call setting1 -->" >
<label>Setting 2:</label> <input type="text"
name="setting2" value="<!--#call setting2 -->" >
<div>
<button type="submit">Save Settings</button>
</div>
</fieldset>
</form>
<h1>Dashboard section</h1>
<label>CPU usage:</label> <span id="cpu_usage">&nbsp;</span>
<div id="graphs"></div>
</div>
</body>
</html>
@@ -0,0 +1,5 @@
/*! jQuery v1.11.3 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */
!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.3",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^Line truncated
return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var Line truncated
return new Za.prototype.init(a,b,c,d,e)}m.Tween=Za,Za.prototype={constructor:Za,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")},cur:function(){var a=Za.propHooks[this.prop];return a&&a.get?a.get(this):Za.propHooks._default.get(this)},run:function(a){var b,c=Za.propHooks[this.prop];return this.options.duration?this.pos=b=m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Za.propHooks._default.set(this),this}},Za.prototype.init.prototype=Za.prototype,Za.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Za.propHooks.scrollTop=Za.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Za.prototype.init,m.fx.step={};var $a,_a,ab=/^(?:toggle|show|hide)$/,bb=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cb=/queueHooks$/,db=[ib],eb={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bb.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bb.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fb(){return setTimeout(function(){$a=void 0}),$a=m.now()}function gb(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hb(a,b,c){for(var d,e=(eb[b]||[]).concat(eb["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ib(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fa(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fa(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ab.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fa(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hb(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jb(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kb(a,b,c){var d,e,f=0,g=db.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$a||fb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$a||fb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jb(k,j.opts.specialEasing);g>f;f++)if(d=db[f].call(j,a,k,j.opts))return d;return m.map(k,hb,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kb,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],eb[cLine truncated
@@ -0,0 +1,8 @@
/* Javascript plotting library for jQuery, version 0.8.3.
Copyright (c) 2007-2014 IOLA and Ole Laursen.
Licensed under the MIT license.
*/
(function($){$.color={};$.color.make=function(r,g,b,a){var o={};o.r=r||0;o.g=g||0;o.b=b||0;o.a=a!=null?a:1;o.add=function(c,d){for(var i=0;i<c.length;++i)o[c.charAt(i)]+=d;return o.normalize()};o.scale=function(c,f){for(var i=0;i<c.length;++i)o[c.charAt(i)]*=f;return o.normalize()};o.toString=function(){if(o.a>=1){return"rgb("+[o.r,o.g,o.b].join(",")+")"}else{return"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"}};o.normalize=function(){function clamp(min,value,max){return value<min?min:value>max?max:value}o.r=clamp(0,parseInt(o.r),255);o.g=clamp(0,parseInt(o.g),255);o.b=clamp(0,parseInt(o.b),255);o.a=clamp(0,o.a,1);return o};o.clone=function(){return $.color.make(o.r,o.b,o.g,o.a)};return o.normalize()};$.color.extract=function(elem,css){var c;do{c=elem.css(css).toLowerCase();if(c!=""&&c!="transparent")break;elem=elem.parent()}while(elem.length&&!$.nodeName(elem.get(0),"body"));if(c=="rgba(0, 0, 0, 0)")c="transparent";return $.color.parse(c)};$.color.parse=function(str){var res,m=$.color.make;if(res=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10));if(res=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10),parseFloat(res[4]));if(res=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55);if(res=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55,parseFloat(res[4]));if(res=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))return m(parseInt(res[1],16),parseInt(res[2],16),parseInt(res[3],16));if(res=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))return m(parseInt(res[1]+res[1],16),parseInt(res[2]+res[2],16),parseInt(res[3]+res[3],16));var name=$.trim(str).toLowerCase();if(name=="transparent")return m(255,255,255,0);else{res=lookupColors[name]||[0,0,0];return m(res[0],res[1],res[2])}};var lookupColors={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);(function($){var hasOwnProperty=Object.prototype.hasOwnProperty;if(!$.fn.detach){$.fn.detach=function(){return this.each(function(){if(this.parentNode){this.parentNode.removeChild(this)}})}}function Canvas(cls,container){var element=container.children("."+cls)[0];if(element==null){element=document.createElement("canvas");element.className=cls;$(element).css({direction:"ltr",position:"absolute",left:0,top:0}).appendTo(container);if(!element.getContext){if(window.G_vmlCanvasManager){element=window.G_vmlCanvasManager.initElement(element)}else{throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode.")}}}this.element=element;var context=this.context=element.getContext("2d");var devicePixelRatio=window.devicePixelRatio||1,backingStoreRatio=context.webkitBackingStorePixelRatio||context.mozBackingStorePixelRatio||context.msBackingStorePixelRatio||context.oBackingStorePixelRatio||context.backingStorePixelRatio||1;this.pixelRatio=devicePixelRatio/backingStoreRatio;this.resize(container.width(),container.height());this.textContainer=null;this.text={};this._textCache={}}Canvas.prototype.resize=function(width,height){if(width<=0||height<=0){throw new Error("Invalid dimensions for plot, width = "+width+", height = "+height)}var element=this.element,context=this.context,pixelRatio=this.pixelRatio;if(this.width!=width){element.width=width*pixelRatio;element.style.width=width+"px";this.width=width}if(this.height!=height){element.height=height*pixelRatio;element.style.height=height+"px";this.height=height}context.restore();context.save();context.scale(pixelRatio,pixelRatio)};Canvas.prototype.clear=function(){this.context.clearRect(0,0,this.width,this.height)};Canvas.prototype.render=function(){vaLine truncated
if(yrange.from==null)yrange.from=yrange.axis.min;if(yrange.to==null)yrange.to=yrange.axis.max;if(xrange.to<xrange.axis.min||xrange.from>xrange.axis.max||yrange.to<yrange.axis.min||yrange.from>yrange.axis.max)continue;xrange.from=Math.max(xrange.from,xrange.axis.min);xrange.to=Math.min(xrange.to,xrange.axis.max);yrange.from=Math.max(yrange.from,yrange.axis.min);yrange.to=Math.min(yrange.to,yrange.axis.max);var xequal=xrange.from===xrange.to,yequal=yrange.from===yrange.to;if(xequal&&yequal){continue}xrange.from=Math.floor(xrange.axis.p2c(xrange.from));xrange.to=Math.floor(xrange.axis.p2c(xrange.to));yrange.from=Math.floor(yrange.axis.p2c(yrange.from));yrange.to=Math.floor(yrange.axis.p2c(yrange.to));if(xequal||yequal){var lineWidth=m.lineWidth||options.grid.markingsLineWidth,subPixel=lineWidth%2?.5:0;ctx.beginPath();ctx.strokeStyle=m.color||options.grid.markingsColor;ctx.lineWidth=lineWidth;if(xequal){ctx.moveTo(xrange.to+subPixel,yrange.from);ctx.lineTo(xrange.to+subPixel,yrange.to)}else{ctx.moveTo(xrange.from,yrange.to+subPixel);ctx.lineTo(xrange.to,yrange.to+subPixel)}ctx.stroke()}else{ctx.fillStyle=m.color||options.grid.markingsColor;ctx.fillRect(xrange.from,yrange.to,xrange.to-xrange.from,yrange.from-yrange.to)}}}axes=allAxes();bw=options.grid.borderWidth;for(var j=0;j<axes.length;++j){var axis=axes[j],box=axis.box,t=axis.tickLength,x,y,xoff,yoff;if(!axis.show||axis.ticks.length==0)continue;ctx.lineWidth=1;if(axis.direction=="x"){x=0;if(t=="full")y=axis.position=="top"?0:plotHeight;else y=box.top-plotOffset.top+(axis.position=="top"?box.height:0)}else{y=0;if(t=="full")x=axis.position=="left"?0:plotWidth;else x=box.left-plotOffset.left+(axis.position=="left"?box.width:0)}if(!axis.innermost){ctx.strokeStyle=axis.options.color;ctx.beginPath();xoff=yoff=0;if(axis.direction=="x")xoff=plotWidth+1;else yoff=plotHeight+1;if(ctx.lineWidth==1){if(axis.direction=="x"){y=Math.floor(y)+.5}else{x=Math.floor(x)+.5}}ctx.moveTo(x,y);ctx.lineTo(x+xoff,y+yoff);ctx.stroke()}ctx.strokeStyle=axis.options.tickColor;ctx.beginPath();for(i=0;i<axis.ticks.length;++i){var v=axis.ticks[i].v;xoff=yoff=0;if(isNaN(v)||v<axis.min||v>axis.max||t=="full"&&(typeof bw=="object"&&bw[axis.position]>0||bw>0)&&(v==axis.min||v==axis.max))continue;if(axis.direction=="x"){x=axis.p2c(v);yoff=t=="full"?-plotHeight:t;if(axis.position=="top")yoff=-yoff}else{y=axis.p2c(v);xoff=t=="full"?-plotWidth:t;if(axis.position=="left")xoff=-xoff}if(ctx.lineWidth==1){if(axis.direction=="x")x=Math.floor(x)+.5;else y=Math.floor(y)+.5}ctx.moveTo(x,y);ctx.lineTo(x+xoff,y+yoff)}ctx.stroke()}if(bw){bc=options.grid.borderColor;if(typeof bw=="object"||typeof bc=="object"){if(typeof bw!=="object"){bw={top:bw,right:bw,bottom:bw,left:bw}}if(typeof bc!=="object"){bc={top:bc,right:bc,bottom:bc,left:bc}}if(bw.top>0){ctx.strokeStyle=bc.top;ctx.lineWidth=bw.top;ctx.beginPath();ctx.moveTo(0-bw.left,0-bw.top/2);ctx.lineTo(plotWidth,0-bw.top/2);ctx.stroke()}if(bw.right>0){ctx.strokeStyle=bc.right;ctx.lineWidth=bw.right;ctx.beginPath();ctx.moveTo(plotWidth+bw.right/2,0-bw.top);ctx.lineTo(plotWidth+bw.right/2,plotHeight);ctx.stroke()}if(bw.bottom>0){ctx.strokeStyle=bc.bottom;ctx.lineWidth=bw.bottom;ctx.beginPath();ctx.moveTo(plotWidth+bw.right,plotHeight+bw.bottom/2);ctx.lineTo(0,plotHeight+bw.bottom/2);ctx.stroke()}if(bw.left>0){ctx.strokeStyle=bc.left;ctx.lineWidth=bw.left;ctx.beginPath();ctx.moveTo(0-bw.left/2,plotHeight+bw.bottom);ctx.lineTo(0-bw.left/2,0);ctx.stroke()}}else{ctx.lineWidth=bw;ctx.strokeStyle=options.grid.borderColor;ctx.strokeRect(-bw/2,-bw/2,plotWidth+bw,plotHeight+bw)}}ctx.restore()}function drawAxisLabels(){$.each(allAxes(),function(_,axis){var box=axis.box,legacyStyles=axis.direction+"Axis "+axis.direction+axis.n+"Axis",layer="flot-"+axis.direction+"-axis flot-"+axis.direction+axis.n+"-axis "+legacyStyles,font=axis.options.font||"flot-tick-label tickLabel",tick,x,y,halign,valign;surface.removeText(layer);if(!axis.show||axis.ticks.length==0)return;for(var i=0;i<axis.ticks.length;++i){tick=axis.ticks[i];if(!tick.label||tick.v<axis.min||tick.v>axis.max)continue;if(axis.direction=="x"){halign="center";x=plotOffset.left+axis.p2c(tick.v);if(axis.position=="bottom"){y=box.top+box.padding}else{y=box.top+box.height-box.padding;valign="bottom"}}else{valign="middle";y=plotOffset.top+axis.p2c(tick.v);if(axis.position=="left"){x=box.left+box.width-box.padding;halign="right"}else{x=box.left+box.padding}}surface.addText(layer,x,y,tick.label,font,null,null,halign,valign)}})}function drawSeries(series){if(series.lines.show)drawSeriesLines(series);if(series.bars.show)drawSeriesBars(series);if(series.points.show)drawSeriesPoints(series)}function drawSeriesLines(series){function plotLine(datapoints,xoffset,yoffset,axisx,axisy){var points=datapoints.points,ps=datapoints.pointsize,prevx=null,prevy=null;ctx.beginPath();for(var i=ps;i<points.length;i+=ps){var x1=points[i-ps],y1=points[i-ps+1],x2=points[i],y2=points[i+1];if(x1==null||x2==null)continue;if(y1<=y2&&y1<axisy.min){if(y2<axLine truncated
@@ -0,0 +1,432 @@
/* Pretty handling of time axes.
Copyright (c) 2007-2014 IOLA and Ole Laursen.
Licensed under the MIT license.
Set axis.mode to "time" to enable. See the section "Time series data" in
API.txt for details.
*/
(function($) {
var options = {
xaxis: {
timezone: null, // "browser" for local to the client or timezone for timezone-js
timeformat: null, // format string to use
twelveHourClock: false, // 12 or 24 time in time mode
monthNames: null // list of names of months
}
};
// round to nearby lower multiple of base
function floorInBase(n, base) {
return base * Math.floor(n / base);
}
// Returns a string with the date d formatted according to fmt.
// A subset of the Open Group's strftime format is supported.
function formatDate(d, fmt, monthNames, dayNames) {
if (typeof d.strftime == "function") {
return d.strftime(fmt);
}
var leftPad = function(n, pad) {
n = "" + n;
pad = "" + (pad == null ? "0" : pad);
return n.length == 1 ? pad + n : n;
};
var r = [];
var escape = false;
var hours = d.getHours();
var isAM = hours < 12;
if (monthNames == null) {
monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
}
if (dayNames == null) {
dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
}
var hours12;
if (hours > 12) {
hours12 = hours - 12;
} else if (hours == 0) {
hours12 = 12;
} else {
hours12 = hours;
}
for (var i = 0; i < fmt.length; ++i) {
var c = fmt.charAt(i);
if (escape) {
switch (c) {
case 'a': c = "" + dayNames[d.getDay()]; break;
case 'b': c = "" + monthNames[d.getMonth()]; break;
case 'd': c = leftPad(d.getDate()); break;
case 'e': c = leftPad(d.getDate(), " "); break;
case 'h': // For back-compat with 0.7; remove in 1.0
case 'H': c = leftPad(hours); break;
case 'I': c = leftPad(hours12); break;
case 'l': c = leftPad(hours12, " "); break;
case 'm': c = leftPad(d.getMonth() + 1); break;
case 'M': c = leftPad(d.getMinutes()); break;
// quarters not in Open Group's strftime specification
case 'q':
c = "" + (Math.floor(d.getMonth() / 3) + 1); break;
case 'S': c = leftPad(d.getSeconds()); break;
case 'y': c = leftPad(d.getFullYear() % 100); break;
case 'Y': c = "" + d.getFullYear(); break;
case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break;
case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break;
case 'w': c = "" + d.getDay(); break;
}
r.push(c);
escape = false;
} else {
if (c == "%") {
escape = true;
} else {
r.push(c);
}
}
}
return r.join("");
}
// To have a consistent view of time-based data independent of which time
// zone the client happens to be in we need a date-like object independent
// of time zones. This is done through a wrapper that only calls the UTC
// versions of the accessor methods.
function makeUtcWrapper(d) {
function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) {
sourceObj[sourceMethod] = function() {
return targetObj[targetMethod].apply(targetObj, arguments);
};
};
var utc = {
date: d
};
// support strftime, if found
if (d.strftime != undefined) {
addProxyMethod(utc, "strftime", d, "strftime");
}
addProxyMethod(utc, "getTime", d, "getTime");
addProxyMethod(utc, "setTime", d, "setTime");
var props = ["Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds"];
for (var p = 0; p < props.length; p++) {
addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]);
addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]);
}
return utc;
};
// select time zone strategy. This returns a date-like object tied to the
// desired timezone
function dateGenerator(ts, opts) {
if (opts.timezone == "browser") {
return new Date(ts);
} else if (!opts.timezone || opts.timezone == "utc") {
return makeUtcWrapper(new Date(ts));
} else if (typeof timezoneJS != "undefined" && typeof timezoneJS.Date != "undefined") {
var d = new timezoneJS.Date();
// timezone-js is fickle, so be sure to set the time zone before
// setting the time.
d.setTimezone(opts.timezone);
d.setTime(ts);
return d;
} else {
return makeUtcWrapper(new Date(ts));
}
}
// map of app. size of time units in milliseconds
var timeUnitSize = {
"second": 1000,
"minute": 60 * 1000,
"hour": 60 * 60 * 1000,
"day": 24 * 60 * 60 * 1000,
"month": 30 * 24 * 60 * 60 * 1000,
"quarter": 3 * 30 * 24 * 60 * 60 * 1000,
"year": 365.2425 * 24 * 60 * 60 * 1000
};
// the allowed tick sizes, after 1 year we use
// an integer algorithm
var baseSpec = [
[1, "second"], [2, "second"], [5, "second"], [10, "second"],
[30, "second"],
[1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"],
[30, "minute"],
[1, "hour"], [2, "hour"], [4, "hour"],
[8, "hour"], [12, "hour"],
[1, "day"], [2, "day"], [3, "day"],
[0.25, "month"], [0.5, "month"], [1, "month"],
[2, "month"]
];
// we don't know which variant(s) we'll need yet, but generating both is
// cheap
var specMonths = baseSpec.concat([[3, "month"], [6, "month"],
[1, "year"]]);
var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"],
[1, "year"]]);
function init(plot) {
plot.hooks.processOptions.push(function (plot, options) {
$.each(plot.getAxes(), function(axisName, axis) {
var opts = axis.options;
if (opts.mode == "time") {
axis.tickGenerator = function(axis) {
var ticks = [];
var d = dateGenerator(axis.min, opts);
var minSize = 0;
// make quarter use a possibility if quarters are
// mentioned in either of these options
var spec = (opts.tickSize && opts.tickSize[1] ===
"quarter") ||
(opts.minTickSize && opts.minTickSize[1] ===
"quarter") ? specQuarters : specMonths;
if (opts.minTickSize != null) {
if (typeof opts.tickSize == "number") {
minSize = opts.tickSize;
} else {
minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]];
}
}
for (var i = 0; i < spec.length - 1; ++i) {
if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]]
+ spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2
&& spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) {
break;
}
}
var size = spec[i][0];
var unit = spec[i][1];
// special-case the possibility of several years
if (unit == "year") {
// if given a minTickSize in years, just use it,
// ensuring that it's an integer
if (opts.minTickSize != null && opts.minTickSize[1] == "year") {
size = Math.floor(opts.minTickSize[0]);
} else {
var magn = Math.pow(10, Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10));
var norm = (axis.delta / timeUnitSize.year) / magn;
if (norm < 1.5) {
size = 1;
} else if (norm < 3) {
size = 2;
} else if (norm < 7.5) {
size = 5;
} else {
size = 10;
}
size *= magn;
}
// minimum size for years is 1
if (size < 1) {
size = 1;
}
}
axis.tickSize = opts.tickSize || [size, unit];
var tickSize = axis.tickSize[0];
unit = axis.tickSize[1];
var step = tickSize * timeUnitSize[unit];
if (unit == "second") {
d.setSeconds(floorInBase(d.getSeconds(), tickSize));
} else if (unit == "minute") {
d.setMinutes(floorInBase(d.getMinutes(), tickSize));
} else if (unit == "hour") {
d.setHours(floorInBase(d.getHours(), tickSize));
} else if (unit == "month") {
d.setMonth(floorInBase(d.getMonth(), tickSize));
} else if (unit == "quarter") {
d.setMonth(3 * floorInBase(d.getMonth() / 3,
tickSize));
} else if (unit == "year") {
d.setFullYear(floorInBase(d.getFullYear(), tickSize));
}
// reset smaller components
d.setMilliseconds(0);
if (step >= timeUnitSize.minute) {
d.setSeconds(0);
}
if (step >= timeUnitSize.hour) {
d.setMinutes(0);
}
if (step >= timeUnitSize.day) {
d.setHours(0);
}
if (step >= timeUnitSize.day * 4) {
d.setDate(1);
}
if (step >= timeUnitSize.month * 2) {
d.setMonth(floorInBase(d.getMonth(), 3));
}
if (step >= timeUnitSize.quarter * 2) {
d.setMonth(floorInBase(d.getMonth(), 6));
}
if (step >= timeUnitSize.year) {
d.setMonth(0);
}
var carry = 0;
var v = Number.NaN;
var prev;
do {
prev = v;
v = d.getTime();
ticks.push(v);
if (unit == "month" || unit == "quarter") {
if (tickSize < 1) {
// a bit complicated - we'll divide the
// month/quarter up but we need to take
// care of fractions so we don't end up in
// the middle of a day
d.setDate(1);
var start = d.getTime();
d.setMonth(d.getMonth() +
(unit == "quarter" ? 3 : 1));
var end = d.getTime();
d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
carry = d.getHours();
d.setHours(0);
} else {
d.setMonth(d.getMonth() +
tickSize * (unit == "quarter" ? 3 : 1));
}
} else if (unit == "year") {
d.setFullYear(d.getFullYear() + tickSize);
} else {
d.setTime(v + step);
}
} while (v < axis.max && v != prev);
return ticks;
};
axis.tickFormatter = function (v, axis) {
var d = dateGenerator(v, axis.options);
// first check global format
if (opts.timeformat != null) {
return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames);
}
// possibly use quarters if quarters are mentioned in
// any of these places
var useQuarters = (axis.options.tickSize &&
axis.options.tickSize[1] == "quarter") ||
(axis.options.minTickSize &&
axis.options.minTickSize[1] == "quarter");
var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
var span = axis.max - axis.min;
var suffix = (opts.twelveHourClock) ? " %p" : "";
var hourCode = (opts.twelveHourClock) ? "%I" : "%H";
var fmt;
if (t < timeUnitSize.minute) {
fmt = hourCode + ":%M:%S" + suffix;
} else if (t < timeUnitSize.day) {
if (span < 2 * timeUnitSize.day) {
fmt = hourCode + ":%M" + suffix;
} else {
fmt = "%b %d " + hourCode + ":%M" + suffix;
}
} else if (t < timeUnitSize.month) {
fmt = "%b %d";
} else if ((useQuarters && t < timeUnitSize.quarter) ||
(!useQuarters && t < timeUnitSize.year)) {
if (span < timeUnitSize.year) {
fmt = "%b";
} else {
fmt = "%b %Y";
}
} else if (useQuarters && t < timeUnitSize.year) {
if (span < timeUnitSize.year) {
fmt = "Q%q";
} else {
fmt = "Q%q %Y";
}
} else {
fmt = "%Y";
}
var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames);
return rt;
};
}
});
});
}
$.plot.plugins.push({
init: init,
options: options,
name: 'time',
version: '1.0'
});
// Time-axis support used to be in Flot core, which exposed the
// formatDate function on the plot object. Various plugins depend
// on the function, so we need to re-expose it here.
$.plot.formatDate = formatDate;
$.plot.dateGenerator = dateGenerator;
})(jQuery);
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

@@ -0,0 +1,75 @@
$(document).ready(function() {
// Start 1-second timer to call RESTful endpoint
setInterval(function() {
$.ajax({
url: '/get_cpu_usage',
dataType: 'json',
success: function(json) {
$('#cpu_usage').text(json.result + '% ');
}
});
}, 1000);
// Initialize graph
var $c = $('<div class="graph"/>').appendTo('#graphs');
var dur = 120;
var past = Date.now() - dur * 1000;
var options = {
lines: {
fill: true,
lineWidth: 0,
fillColor: {colors: [ { opacity: 0 }, { opacity: 1 } ] }
},
grid: { borderWidth: 1, borderColor: '#ccc'},
xaxis: { mode: 'time', ticks: 5 },
legend: { labelBoxBorderColor: '#fff' },
colors: [ '#fec', '#396', '#e39', '9e2' ],
hooks: {
draw: [function(plot, canvas) {
canvas.font = '13px sans-serif';
canvas.fillStyle = '#aaa';
canvas.fillText('Resource Usage', 35, 25);
}]
}
};
var plot = $.plot($c, [], options);
var updateGraph = function(counter) {
var data = plot.getData();
var now = Date.now();
var oldest = now - dur * 1000;
data[0] = {
show: false,
data: [[oldest, null], [now, null]]
};
console.log(data);
// Remove old points
$.each(data, function(di, d) {
while (d.data.length > 0 && d.data[0][0] < oldest) {
d.data.shift();
}
});
// Add new points
var new_datapoint = [now, counter];
if (data[1]) {
data[1].label = 'memory';
data[1].data.push(new_datapoint);
} else {
data[1] = [{ data: [new_datapoint] }];
}
// Redraw the graph
plot.setData(data);
plot.setupGrid();
plot.draw();
};
// Create Websocket connection. For simplicity, no reconnect logic is here.
var ws = new WebSocket('ws://' + location.host);
ws.onmessage = function(ev) {
updateGraph(ev.data);
};
});
@@ -0,0 +1,87 @@
* {
outline: none;
}
body {
background: #36c;
color: #555;
margin: 0;
padding: 0;
font: 16px/1.4 Helvetica, Arial, sans-serif;
}
div.content {
width: 800px;
margin: 1em auto;
padding: 2em;
background-color: #fff;
border-radius: 1em;
}
fieldset {
border: 1px solid #ccc;
padding: 1em;
}
fieldset legend {
font-style: italic;
color: silver;
}
fieldset label {
text-align: right;
padding: 0 1em;
}
input[type=text] {
font-size: 100%;
padding: 0.2em;
border: 1px solid #ccc;
}
button {
color: #666;
background: #eee;
border: 1px solid #ccc;
padding: 0.2em 2em;
font-size: 100%;
cursor: pointer;
margin-top: 1em;
}
button:hover {
background: #ccc;
}
code {
background: #f0f0f0;
padding: 0 0.3em;
color: #396;
border-radius: 0.3em;
}
a:link, a:visited {
color: #69c;
text-decoration: none;
}
#graphs {
margin-top: 1em;
}
.graph {
min-height: 12em;
border: 0px solid #ccc;
position: relative;
margin: 0 1em 1em 0;
width: 36em;
display: inline-block;
}
.graph_title {
position: absolute;
top: 0.5em; left: 1em;
color: #ccc;
font-weight: bold;
z-index: 999;
}
-12
View File
@@ -1,12 +0,0 @@
# 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
@@ -1,96 +0,0 @@
// 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;
}
-44
View File
@@ -1,44 +0,0 @@
<!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
@@ -1,43 +0,0 @@
// 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
@@ -1,68 +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_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
@@ -1,12 +0,0 @@
# 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
@@ -1,36 +0,0 @@
#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;
}
+13
View File
@@ -0,0 +1,13 @@
SOURCES = $(PROG).c ../../mongoose.c
CFLAGS = -W -Wall -I../.. -Wno-unused-function $(CFLAGS_EXTRA) $(MODULE_CFLAGS)
all: $(PROG)
$(PROG): $(SOURCES)
$(CC) $(SOURCES) -o $@ $(CFLAGS)
$(PROG).exe: $(SOURCES)
cl $(SOURCES) /I../.. /MD /Fe$@
clean:
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
-20
View File
@@ -1,20 +0,0 @@
# 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*
-59
View File
@@ -1,59 +0,0 @@
// Copyright (c) 2004-2012 Sergey Lyubka
// This file is a part of mongoose project, http://github.com/valenok/mongoose
#include <stdio.h>
#include <string.h>
#include "mongoose.h"
static int send_index_page(struct mg_connection *conn) {
const char *data;
int data_len, ofs = 0;
char var_name[100], file_name[100];
mg_printf_data(conn, "%s",
"<html><body>Upload example."
"<form method=\"POST\" action=\"/handle_post_request\" "
" enctype=\"multipart/form-data\">"
"<input type=\"file\" name=\"file\" /> <br/>"
"<input type=\"submit\" value=\"Upload\" />"
"</form>");
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_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, 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;
}
-12
View File
@@ -1,12 +0,0 @@
# 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
-62
View File
@@ -1,62 +0,0 @@
#include <stdio.h>
#include <string.h>
#include "mongoose.h"
static const char *html_form =
"<html><body>POST example."
"<form method=\"POST\" action=\"/handle_post_request\">"
"Input 1: <input type=\"text\" name=\"input_1\" /> <br/>"
"Input 2: <input type=\"text\" name=\"input_2\" /> <br/>"
"<input type=\"submit\" />"
"</form></body></html>";
static void send_reply(struct mg_connection *conn) {
char var1[500], var2[500];
if (strcmp(conn->uri, "/handle_post_request") == 0) {
// User has submitted a form, show submitted data and a variable value
// Parse form data. var1 and var2 are guaranteed to be NUL-terminated
mg_get_var(conn, "input_1", var1, sizeof(var1));
mg_get_var(conn, "input_2", var2, sizeof(var2));
// Send reply to the client, showing submitted form values.
// POST data is in conn->content, data length is in conn->content_len
mg_send_header(conn, "Content-Type", "text/plain");
mg_printf_data(conn,
"Submitted data: [%.*s]\n"
"Submitted data length: %d bytes\n"
"input_1: [%s]\n"
"input_2: [%s]\n",
conn->content_len, conn->content,
conn->content_len, var1, var2);
} else {
// Show HTML form.
mg_send_data(conn, html_form, strlen(html_form));
}
}
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, 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;
}
-21
View File
@@ -1,21 +0,0 @@
# 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
@@ -1,38 +0,0 @@
// 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;
}
+9 -7
View File
@@ -1,12 +1,14 @@
# Copyright (c) 2014 Cesanta Software
# All rights reserved
PROG = http_client PROG = http_client
CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
SOURCES = $(PROG).c ../../mongoose.c SOURCES = $(PROG).c ../../mongoose.c
CFLAGS = -W -Wall -I../.. -DMG_ENABLE_SSL -lssl -lcrypto -pthread $(CFLAGS_EXTRA)
unix: $(SOURCES) all: $(PROG)
$(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
$(PROG): $(SOURCES)
$(CC) $(SOURCES) -o $@ $(CFLAGS)
$(PROG).exe: $(SOURCES)
cl $(SOURCES) /I../.. /MD /Fe$@
clean: clean:
rm -rf $(PROG) *.exe *.dSYM *.obj *.exp *.o *.lib rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
+54 -66
View File
@@ -1,82 +1,70 @@
// Copyright (c) 2014 Cesanta Software /*
// All rights reserved * Copyright (c) 2014 Cesanta Software Limited
// * All rights reserved
// This example demostrates how to connect to the remote Web server, *
// download data, process it and send back a reply. * This program fetches HTTP URLs.
*/
#include <signal.h>
#include <stdlib.h>
#include "mongoose.h" #include "mongoose.h"
static int s_received_signal = 0; static int s_exit_flag = 0;
static struct mg_server *s_server = NULL; static int s_show_headers = 0;
static const char *s_remote_addr = "glosbe.com:80"; static const char *s_show_headers_opt = "--show-headers";
static void signal_handler(int sig_num) { static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
signal(sig_num, signal_handler); struct http_message *hm = (struct http_message *) ev_data;
s_received_signal = sig_num;
}
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
struct mg_connection *client, *orig;
switch (ev) { switch (ev) {
case MG_AUTH: case MG_EV_CONNECT:
return MG_TRUE; if (* (int *) ev_data != 0) {
fprintf(stderr, "connect() failed: %s\n", strerror(* (int *) ev_data));
case MG_CONNECT: s_exit_flag = 1;
// 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;
} }
break;
case MG_EV_HTTP_REPLY:
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
if (s_show_headers) {
fwrite(hm->message.p, 1, hm->message.len, stdout);
} else {
fwrite(hm->body.p, 1, hm->body.len, stdout);
}
putchar('\n');
s_exit_flag = 1;
break;
default: default:
return MG_FALSE; break;
} }
} }
int main(void) { int main(int argc, char *argv[]) {
s_server = mg_create_server(NULL, ev_handler); struct mg_mgr mgr;
int i;
mg_set_option(s_server, "listening_port", "8080"); mg_mgr_init(&mgr, NULL);
// Setup signal handlers /* Process command line arguments */
signal(SIGTERM, signal_handler); for (i = 1; i < argc; i++) {
signal(SIGINT, signal_handler); if (strcmp(argv[i], s_show_headers_opt) == 0) {
s_show_headers = 1;
printf("Listening on port %s\n", mg_get_option(s_server, "listening_port")); } else if (strcmp(argv[i], "--hexdump") == 0 && i + 1 < argc) {
while (s_received_signal == 0) { mgr.hexdump_file = argv[++i];
mg_poll_server(s_server, 1000); } else {
break;
}
} }
mg_destroy_server(&s_server);
printf("Existing on signal %d\n", s_received_signal);
return EXIT_SUCCESS; if (i + 1 != argc) {
fprintf(stderr, "Usage: %s [%s] [--hexdump <file>] <URL>\n",
argv[0], s_show_headers_opt);
exit(EXIT_FAILURE);
}
mg_connect_http(&mgr, ev_handler, argv[i], NULL, NULL);
while (s_exit_flag == 0) {
mg_mgr_poll(&mgr, 1000);
}
mg_mgr_free(&mgr);
return 0;
} }
+163
View File
@@ -0,0 +1,163 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\mongoose.c">
<WarningLevel Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">TurnOffAllWarnings</WarningLevel>
<WarningLevel Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">TurnOffAllWarnings</WarningLevel>
</ClCompile>
<ClCompile Include="http_client.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\mongoose.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{BDF3FC6F-2658-4A27-846D-6A45FB53C602}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>http_client</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;MG_ENABLE_THREADS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>./../../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;MG_ENABLE_THREADS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>./../../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
+14
View File
@@ -0,0 +1,14 @@
PROG = json_rpc_server
SOURCES = $(PROG).c ../../mongoose.c
CFLAGS = -W -Wall -I../.. -pthread $(CFLAGS_EXTRA)
all: $(PROG)
$(PROG): $(SOURCES)
$(CC) $(SOURCES) -o $@ $(CFLAGS)
$(PROG).exe: $(SOURCES)
cl $(SOURCES) /I../.. /MD /Fe$@
clean:
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*
* To test this server, do
* $ curl -d '{"id":1,method:"sum",params:[22,33]}' 127.0.0.1:8000
*/
#include "mongoose.h"
static const char *s_http_port = "8000";
static int rpc_sum(char *buf, int len, struct mg_rpc_request *req) {
double sum = 0;
int i;
if (req->params[0].type != JSON_TYPE_ARRAY) {
return mg_rpc_create_std_error(buf, len, req,
JSON_RPC_INVALID_PARAMS_ERROR);
}
for (i = 0; i < req->params[0].num_desc; i++) {
if (req->params[i + 1].type != JSON_TYPE_NUMBER) {
return mg_rpc_create_std_error(buf, len, req,
JSON_RPC_INVALID_PARAMS_ERROR);
}
sum += strtod(req->params[i + 1].ptr, NULL);
}
return mg_rpc_create_reply(buf, len, req, "f", sum);
}
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct http_message *hm = (struct http_message *) ev_data;
static const char *methods[] = { "sum", NULL };
static mg_rpc_handler_t handlers[] = { rpc_sum, NULL };
char buf[100];
switch (ev) {
case MG_EV_HTTP_REQUEST:
mg_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf),
methods, handlers);
mg_printf(nc, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n"
"Content-Type: application/json\r\n\r\n%s",
(int) strlen(buf), buf);
nc->flags |= MG_F_SEND_AND_CLOSE;
break;
default:
break;
}
}
int main(void) {
struct mg_mgr mgr;
struct mg_connection *nc;
mg_mgr_init(&mgr, NULL);
nc = mg_bind(&mgr, s_http_port, ev_handler);
mg_set_protocol_http_websocket(nc);
printf("Starting JSON-RPC server on port %s\n", s_http_port);
for (;;) {
mg_mgr_poll(&mgr, 1000);
}
mg_mgr_free(&mgr);
return 0;
}
+10
View File
@@ -0,0 +1,10 @@
FROM cesanta/mongoose
COPY load_balancer.c /mongoose/
WORKDIR /mongoose
RUN mkdir /mongoose/certs; \
sed -i 's:#include "../../mongoose.h":#include "mongoose.h":' load_balancer.c; \
cc load_balancer.c mongoose.c -o load_balancer -W -Wall -pthread -DMG_ENABLE_SSL -lssl -lcrypto
EXPOSE 8000
VOLUME ["/mongoose/certs"]
ENTRYPOINT ["/mongoose/load_balancer"]
+39
View File
@@ -0,0 +1,39 @@
# To build with SSL under windows, do:
# wine make load_balancer.exe SSL=openssl # OpenSSL build
# wine make load_balancer.exe SSL=krypton # Krypton build
PROG = load_balancer
SOURCES = $(PROG).c ../../mongoose.c
CFLAGS = -W -Wall -pthread $(CFLAGS_EXTRA)
ifeq ($(SSL), openssl)
OPENSSL_PATH = ./openssl-0.9.8
CFLAGS_EXTRA += -DMG_ENABLE_SSL -I$(OPENSSL_PATH)/include
CFLAGS_EXTRA += /link /libpath:$(OPENSSL_PATH)/lib ssleay32.lib libeay32.lib
endif
ifeq ($(SSL), krypton)
KRYPTON_PATH = ../../../krypton
CFLAGS_EXTRA += -DMG_ENABLE_SSL $(KRYPTON_PATH)/krypton.c -I$(KRYPTON_PATH)
endif
all: $(PROG)
$(PROG): $(SOURCES)
$(CC) $(SOURCES) -o $@ $(CFLAGS)
$(PROG).exe: $(SOURCES)
cl $(SOURCES) /I.. /MD /Fe$@ /DMG_ENABLE_THREADS advapi32.lib $(CFLAGS_EXTRA)
test: $(PROG)
$(MAKE) -C ../api_server
sh unit_test.sh $$(pwd)/$(PROG)
docker-build:
docker build -t cesanta/load_balancer .
docker-push:
docker push cesanta/load_balancer
clean:
rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG)
+44
View File
@@ -0,0 +1,44 @@
# Mongoose-based HTTP load balancer
## Configuration
Load balancer is configured with command-line flags.
### Global flags
* `-p port` – TCP port to listen on. Default: 8000.
* `-l log_file` – path to the log file. Default: none.
* `-s ssl_cert` – path to SSL certificate. Default: none.
### Backend configuration
Main flag is `-b uri_prefix host_port` – it adds a new backend for a given
URI prefix. Example: `-b /stuff/ 127.0.0.1:8080` will route all requests that
start with '/stuff/' to a backend at port 8080 on localhost. There is a special
syntax for `uri_prefix` that allows you to change the URIs that get passed to
backends:
* `-b /stuff/=/ 127.0.0.1:8080` – for '/stuff/thing' backend will see '/thing'.
* `-b /stuff/=/other/ 127.0.0.1:8080` – '/stuff/thing' => '/other/thing'.
Also there are few per-backend flags that can be placed before `-b` and apply
only to the next backend:
* `-r` – instead of proxying requests load balancer will reply with 302
redirect.
* `-v vhost` – match not only URI prefix but 'Host:' header as well.
### Example
```
load_balancer -s path/to/cert.pem \
-v example.com -b /site/=/ 127.0.0.1:8080 \
-b /static/ 127.0.0.1:8081 \
-b /static/ 127.0.0.1:8082
```
In this example requests to 'example.com/site/' will be forwarded to the
backend on port 8080 with '/site' prefix stripped off and requests to
'/static/' on any virtual host will be balanced in round-robin fashion between
backends on ports 8081 and 8082.
+645
View File
@@ -0,0 +1,645 @@
/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#include "../../mongoose.h"
#include <sys/queue.h>
#define MAX_IDLE_CONNS 5
#define CONN_IDLE_TIMEOUT 30
struct http_backend;
struct be_conn {
struct http_backend *be;
struct mg_connection *nc;
time_t idle_deadline;
STAILQ_ENTRY(be_conn) conns;
};
STAILQ_HEAD(be_conn_list_head, be_conn);
struct http_backend {
const char *vhost; /* NULL if any host */
const char *uri_prefix; /* URI prefix, e.g. "/api/v1/", "/static/" */
const char *uri_prefix_replacement; /* if not NULL, will replace uri_prefix in
requests to backends */
const char *host_port; /* Backend address */
int redirect; /* if true redirect instead of proxy */
int usage_counter; /* Number of times this backend was chosen */
struct be_conn_list_head conns;
int num_conns;
};
struct peer {
struct mg_connection *nc;
int64_t body_len; /* Size of the HTTP body to forward */
int64_t body_sent; /* Number of bytes already forwarded */
struct {
/* Headers have been sent, no more headers. */
unsigned int headers_sent : 1;
unsigned int keep_alive : 1;
} flags;
};
struct conn_data {
struct be_conn *be_conn; /* Chosen backend */
struct peer client; /* Client peer */
struct peer backend; /* Backend peer */
time_t last_activity;
};
static const char *s_error_500 = "HTTP/1.1 500 Failed\r\n";
static const char *s_content_len_0 = "Content-Length: 0\r\n";
static const char *s_connection_close = "Connection: close\r\n";
static const char *s_http_port = "8000";
static struct http_backend s_vhost_backends[100], s_default_backends[100];
static int s_num_vhost_backends = 0, s_num_default_backends = 0;
static int s_sig_num = 0;
static int s_backend_keepalive = 0;
static FILE *s_log_file = NULL;
#ifdef MG_ENABLE_SSL
const char *s_ssl_cert = NULL;
#endif
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data);
static void write_log(const char *fmt, ...);
static void signal_handler(int sig_num) {
signal(sig_num, signal_handler);
s_sig_num = sig_num;
}
static void send_http_err(struct mg_connection *nc, const char *err_line) {
mg_printf(nc, "%s%s%s\r\n", err_line, s_content_len_0, s_connection_close);
}
static void respond_with_error(struct conn_data *conn, const char *err_line) {
struct mg_connection *nc = conn->client.nc;
int headers_sent = conn->client.flags.headers_sent;
#ifdef DEBUG
write_log("conn=%p nc=%p respond_with_error %d\n", conn, nc, headers_sent);
#endif
if (nc == NULL) return;
if (!headers_sent) {
send_http_err(nc, err_line);
conn->client.flags.headers_sent = 1;
}
nc->flags |= MG_F_SEND_AND_CLOSE;
}
static int has_prefix(const struct mg_str *uri, const char *prefix) {
size_t prefix_len = strlen(prefix);
return uri->len >= prefix_len && memcmp(uri->p, prefix, prefix_len) == 0;
}
static int matches_vhost(const struct mg_str *host, const char *vhost) {
size_t vhost_len;
if (vhost == NULL) {
return 1;
}
vhost_len = strlen(vhost);
return host->len == vhost_len && memcmp(host->p, vhost, vhost_len) == 0;
}
static void write_log(const char *fmt, ...) {
va_list ap;
if (s_log_file != NULL) {
va_start(ap, fmt);
vfprintf(s_log_file, fmt, ap);
fflush(s_log_file);
va_end(ap);
}
}
static struct http_backend *choose_backend_from_list(
struct http_message *hm, struct http_backend *backends, int num_backends) {
int i;
struct mg_str vhost = {"", 0};
const struct mg_str *host = mg_get_http_header(hm, "host");
if (host != NULL) vhost = *host;
const char *vhost_end = vhost.p;
while (vhost_end < vhost.p + vhost.len && *vhost_end != ':') {
vhost_end++;
}
vhost.len = vhost_end - vhost.p;
struct http_backend *chosen = NULL;
for (i = 0; i < num_backends; i++) {
struct http_backend *be = &backends[i];
if (has_prefix(&hm->uri, be->uri_prefix) &&
matches_vhost(&vhost, be->vhost) &&
(chosen == NULL ||
/* Prefer most specific URI prefixes */
strlen(be->uri_prefix) > strlen(chosen->uri_prefix) ||
/* Among prefixes of the same length chose the least used. */
(strlen(be->uri_prefix) == strlen(chosen->uri_prefix) &&
be->usage_counter < chosen->usage_counter))) {
chosen = be;
}
}
return chosen;
}
static struct http_backend *choose_backend(struct http_message *hm) {
struct http_backend *chosen =
choose_backend_from_list(hm, s_vhost_backends, s_num_vhost_backends);
/* Nothing was chosen for this vhost, look for vhost == NULL backends. */
if (chosen == NULL) {
chosen = choose_backend_from_list(hm, s_default_backends,
s_num_default_backends);
}
if (chosen != NULL) chosen->usage_counter++;
return chosen;
}
static void forward_body(struct peer *src, struct peer *dst) {
struct mbuf *src_io = &src->nc->recv_mbuf;
if (src->body_sent < src->body_len) {
size_t to_send = src->body_len - src->body_sent;
if (src_io->len < to_send) {
to_send = src_io->len;
}
mg_send(dst->nc, src_io->buf, to_send);
src->body_sent += to_send;
mbuf_remove(src_io, to_send);
}
#ifdef DEBUG
write_log("forward_body %p (ka=%d) -> %p sent %d of %d\n", src->nc,
src->flags.keep_alive, dst->nc, src->body_sent, src->body_len);
#endif
}
static void forward(struct conn_data *conn, struct http_message *hm,
struct peer *src_peer, struct peer *dst_peer) {
struct mg_connection *src = src_peer->nc;
struct mg_connection *dst = dst_peer->nc;
struct mbuf *io = &src->recv_mbuf;
int i;
int is_request = (src_peer == &conn->client);
src_peer->body_len = hm->body.len;
struct http_backend *be = conn->be_conn->be;
if (is_request) {
/* Write rewritten request line. */
size_t trim_len = strlen(be->uri_prefix);
mg_printf(dst, "%.*s%s%.*s\r\n", (int) (hm->uri.p - io->buf), io->buf,
be->uri_prefix_replacement,
(int) (hm->proto.p + hm->proto.len - (hm->uri.p + trim_len)),
hm->uri.p + trim_len);
} else {
/* Reply line goes without modification */
mg_printf(dst, "%.*s %d %.*s\r\n", (int) hm->proto.len, hm->proto.p,
(int) hm->resp_code, (int) hm->resp_status_msg.len,
hm->resp_status_msg.p);
}
/* Headers. */
for (i = 0; i < MG_MAX_HTTP_HEADERS && hm->header_names[i].len > 0; i++) {
struct mg_str hn = hm->header_names[i];
struct mg_str hv = hm->header_values[i];
#ifdef MG_ENABLE_SSL
/*
* If we terminate SSL and backend redirects to local HTTP port,
* strip protocol to let client use HTTPS.
* TODO(lsm): web page content may also contain local HTTP references,
* they need to be rewritten too.
*/
if (mg_vcasecmp(&hn, "Location") == 0 && s_ssl_cert != NULL) {
size_t hlen = strlen(be->host_port);
const char *hp = be->host_port, *p = memchr(hp, ':', hlen);
if (p == NULL) {
p = hp + hlen;
}
if (mg_ncasecmp(hv.p, "http://", 7) == 0 &&
mg_ncasecmp(hv.p + 7, hp, (p - hp)) == 0) {
mg_printf(dst, "Location: %.*s\r\n", (int) (hv.len - (7 + (p - hp))),
hv.p + 7 + (p - hp));
continue;
}
}
#endif
/* We always rewrite the connection header depending on the settings. */
if (mg_vcasecmp(&hn, "Connection") == 0) continue;
/* Don't pass chunked transfer encoding to the client */
if (mg_vcasecmp(&hn, "Transfer-encoding") == 0 &&
mg_vcasecmp(&hv, "chunked") == 0) {
continue;
}
mg_printf(dst, "%.*s: %.*s\r\n", (int) hn.len, hn.p, (int) hv.len, hv.p);
}
/* Emit the connection header. */
const char *connection_mode = "close";
if (dst_peer == &conn->backend) {
if (s_backend_keepalive) connection_mode = "keep-alive";
} else {
if (conn->client.flags.keep_alive) connection_mode = "keep-alive";
}
mg_printf(dst, "Connection: %s\r\n", connection_mode);
mg_printf(dst, "%s", "\r\n");
mbuf_remove(io, hm->body.p - hm->message.p); /* We've forwarded headers */
dst_peer->flags.headers_sent = 1;
forward_body(src_peer, dst_peer);
}
struct be_conn *get_conn(struct http_backend *be) {
if (STAILQ_EMPTY(&be->conns)) return NULL;
struct be_conn *result = STAILQ_FIRST(&be->conns);
STAILQ_REMOVE_HEAD(&be->conns, conns);
be->num_conns--;
return result;
}
/*
* choose_backend parses incoming HTTP request and routes it to the appropriate
* backend. It assumes that clients don't do HTTP pipelining, handling only
* one request request for each connection. To give a hint to backend about
* this it inserts "Connection: close" header into each forwarded request.
*/
static int connect_backend(struct conn_data *conn, struct http_message *hm) {
struct mg_connection *nc = conn->client.nc;
struct http_backend *be = choose_backend(hm);
write_log("%.*s %.*s backend=%s\n", (int) hm->method.len, hm->method.p,
(int) hm->uri.len, hm->uri.p, be ? be->host_port : "not defined");
if (be == NULL) return 0;
if (be->redirect != 0) {
mg_printf(nc, "HTTP/1.1 302 Found\r\nLocation: %s\r\n\r\n", be->host_port);
return 1;
}
struct be_conn *bec = get_conn(be);
if (bec != NULL) {
bec->nc->handler = ev_handler;
#ifdef DEBUG
write_log("conn=%p to %p (%s) reusing bec=%p\n", conn, be, be->host_port,
bec);
#endif
} else {
bec = malloc(sizeof(*conn->be_conn));
memset(bec, 0, sizeof(*bec));
bec->nc = mg_connect(nc->mgr, be->host_port, ev_handler);
#ifdef DEBUG
write_log("conn=%p new conn to %p (%s) bec=%p\n", conn, be, be->host_port,
bec);
#endif
if (bec->nc == NULL) {
free(bec);
write_log("Connection to [%s] failed\n", be->host_port);
return 0;
}
}
bec->be = be;
conn->be_conn = bec;
conn->backend.nc = bec->nc;
conn->backend.nc->user_data = conn;
mg_set_protocol_http_websocket(conn->backend.nc);
return 1;
}
static int is_keep_alive(struct http_message *hm) {
const struct mg_str *connection_header = mg_get_http_header(hm, "Connection");
if (connection_header == NULL) {
/* HTTP/1.1 connections are keep-alive by default. */
if (mg_vcasecmp(&hm->proto, "HTTP/1.1") != 0) return 0;
} else if (mg_vcasecmp(connection_header, "keep-alive") != 0) {
return 0;
}
// We must also have Content-Length.
return mg_get_http_header(hm, "Content-Length") != NULL;
}
static void idle_backend_handler(struct mg_connection *nc, int ev,
void *ev_data) {
(void) ev_data; /* Unused. */
struct be_conn *bec = nc->user_data;
const time_t now = time(NULL);
#ifdef DEBUG
write_log("%d idle bec=%p nc=%p ev=%d deadline=%d\n", now, bec, nc, ev,
bec->idle_deadline);
#endif
switch (ev) {
case MG_EV_POLL: {
if (bec->idle_deadline > 0 && now > bec->idle_deadline) {
#ifdef DEBUG
write_log("bec=%p nc=%p closing due to idleness\n", bec, bec->nc);
#endif
bec->nc->flags |= MG_F_CLOSE_IMMEDIATELY;
}
break;
}
case MG_EV_CLOSE: {
#ifdef DEBUG
write_log("bec=%p closed\n", bec);
#endif
if (bec->idle_deadline > 0) {
STAILQ_REMOVE(&bec->be->conns, bec, be_conn, conns);
}
free(bec);
break;
}
}
}
void release_backend(struct conn_data *conn) {
/* Disassociate the backend, put back on the pool. */
struct be_conn *bec = conn->be_conn;
conn->be_conn = NULL;
if (bec->nc == NULL) {
free(bec);
memset(&conn->backend, 0, sizeof(conn->backend));
return;
}
struct http_backend *be = bec->be;
bec->nc->user_data = bec;
bec->nc->handler = idle_backend_handler;
if (conn->backend.flags.keep_alive) {
bec->idle_deadline = time(NULL) + CONN_IDLE_TIMEOUT;
STAILQ_INSERT_TAIL(&be->conns, bec, conns);
#ifdef DEBUG
write_log("bec=%p becoming idle\n", bec);
#endif
be->num_conns++;
while (be->num_conns > MAX_IDLE_CONNS) {
bec = STAILQ_FIRST(&be->conns);
STAILQ_REMOVE_HEAD(&be->conns, conns);
be->num_conns--;
bec->idle_deadline = 0;
bec->nc->flags = MG_F_CLOSE_IMMEDIATELY;
#ifdef DEBUG
write_log("bec=%p evicted\n", bec);
#endif
}
} else {
bec->idle_deadline = 0;
bec->nc->flags |= MG_F_CLOSE_IMMEDIATELY;
}
memset(&conn->backend, 0, sizeof(conn->backend));
}
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
struct conn_data *conn = (struct conn_data *) nc->user_data;
const time_t now = time(NULL);
#ifdef DEBUG
write_log("%d conn=%p nc=%p ev=%d ev_data=%p bec=%p bec_nc=%p\n", now, conn,
nc, ev, ev_data, conn != NULL ? conn->be_conn : NULL,
conn != NULL && conn->be_conn != NULL ? conn->be_conn->nc : NULL);
#endif
if (conn == NULL) {
if (ev == MG_EV_ACCEPT) {
conn = calloc(1, sizeof(*conn));
if (conn == NULL) {
send_http_err(nc, s_error_500);
} else {
memset(conn, 0, sizeof(*conn));
nc->user_data = conn;
conn->client.nc = nc;
conn->client.body_len = -1;
conn->backend.body_len = -1;
conn->last_activity = now;
}
return;
} else {
if (ev != MG_EV_POLL) {
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
}
return;
}
}
if (ev != MG_EV_POLL) conn->last_activity = now;
switch (ev) {
case MG_EV_HTTP_REQUEST: { /* From client */
assert(conn != NULL);
assert(conn->be_conn == NULL);
struct http_message *hm = (struct http_message *) ev_data;
conn->client.flags.keep_alive = is_keep_alive(hm);
if (!connect_backend(conn, hm)) {
respond_with_error(conn, s_error_500);
break;
}
if (conn->backend.nc == NULL) {
/* This is a redirect, we're done. */
conn->client.nc->flags |= MG_F_SEND_AND_CLOSE;
break;
}
forward(conn, hm, &conn->client, &conn->backend);
break;
}
case MG_EV_CONNECT: { /* To backend */
assert(conn != NULL);
assert(conn->be_conn != NULL);
int status = *(int *) ev_data;
if (status != 0) {
write_log("Error connecting to %s: %d (%s)\n",
conn->be_conn->be->host_port, status, strerror(status));
/* TODO(lsm): mark backend as defunct, try it later on */
respond_with_error(conn, s_error_500);
conn->be_conn->nc = NULL;
release_backend(conn);
break;
}
break;
}
case MG_EV_HTTP_REPLY: { /* From backend */
assert(conn != NULL);
struct http_message *hm = (struct http_message *) ev_data;
conn->backend.flags.keep_alive = s_backend_keepalive && is_keep_alive(hm);
forward(conn, hm, &conn->backend, &conn->client);
release_backend(conn);
if (!conn->client.flags.keep_alive) {
conn->client.nc->flags |= MG_F_SEND_AND_CLOSE;
} else {
#ifdef DEBUG
write_log("conn=%p remains open\n", conn);
#endif
}
break;
}
case MG_EV_POLL: {
assert(conn != NULL);
if (now - conn->last_activity > CONN_IDLE_TIMEOUT &&
conn->backend.nc == NULL /* not waiting for backend */) {
#ifdef DEBUG
write_log("conn=%p has been idle for too long\n", conn);
conn->client.nc->flags |= MG_F_SEND_AND_CLOSE;
#endif
}
break;
}
case MG_EV_CLOSE: {
assert(conn != NULL);
if (nc == conn->client.nc) {
#ifdef DEBUG
write_log("conn=%p nc=%p client closed, body_sent=%d\n", conn, nc,
conn->backend.body_sent);
#endif
conn->client.nc = NULL;
if (conn->backend.nc != NULL) {
conn->backend.nc->flags |= MG_F_CLOSE_IMMEDIATELY;
}
} else if (nc == conn->backend.nc) {
#ifdef DEBUG
write_log("conn=%p nc=%p backend closed\n", conn, nc);
#endif
conn->backend.nc = NULL;
if (conn->client.nc != NULL &&
(conn->backend.body_len < 0 ||
conn->backend.body_sent < conn->backend.body_len)) {
write_log("Backend %s disconnected.\n", conn->be_conn->be->host_port);
respond_with_error(conn, s_error_500);
}
}
if (conn->client.nc == NULL && conn->backend.nc == NULL) {
free(conn);
}
break;
}
}
}
static void print_usage_and_exit(const char *prog_name) {
fprintf(stderr,
"Usage: %s [-D debug_dump_file] [-p http_port] [-l log] [-k]"
#if MG_ENABLE_SSL
"[-s ssl_cert] "
#endif
"<[-r] [-v vhost] -b uri_prefix[=replacement] host_port> ... \n",
prog_name);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]) {
struct mg_mgr mgr;
struct mg_connection *nc;
int i, redirect = 0;
const char *vhost = NULL;
mg_mgr_init(&mgr, NULL);
/* Parse command line arguments */
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-D") == 0) {
mgr.hexdump_file = argv[i + 1];
i++;
} else if (strcmp(argv[i], "-k") == 0) {
s_backend_keepalive = 1;
} else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) {
if (strcmp(argv[i + 1], "-") == 0) {
s_log_file = stdout;
} else {
s_log_file = fopen(argv[i + 1], "a");
if (s_log_file == NULL) {
perror("fopen");
exit(EXIT_FAILURE);
}
}
i++;
} else if (strcmp(argv[i], "-p") == 0) {
s_http_port = argv[i + 1];
i++;
} else if (strcmp(argv[i], "-r") == 0 && i + 1 < argc) {
redirect = 1;
} else if (strcmp(argv[i], "-v") == 0 && i + 1 < argc) {
if (strcmp(argv[i + 1], "") == 0) {
vhost = NULL;
} else {
vhost = argv[i + 1];
}
i++;
} else if (strcmp(argv[i], "-b") == 0 && i + 2 < argc) {
struct http_backend *be =
vhost != NULL ? &s_vhost_backends[s_num_vhost_backends++]
: &s_default_backends[s_num_default_backends++];
STAILQ_INIT(&be->conns);
char *r = NULL;
be->vhost = vhost;
be->uri_prefix = argv[i + 1];
be->host_port = argv[i + 2];
be->redirect = redirect;
be->uri_prefix_replacement = be->uri_prefix;
if ((r = strchr(be->uri_prefix, '=')) != NULL) {
*r = '\0';
be->uri_prefix_replacement = r + 1;
}
printf(
"Adding backend for %s%s : %s "
"[redirect=%d,prefix_replacement=%s]\n",
be->vhost == NULL ? "" : be->vhost, be->uri_prefix, be->host_port,
be->redirect, be->uri_prefix_replacement);
vhost = NULL;
redirect = 0;
i += 2;
#ifdef MG_ENABLE_SSL
} else if (strcmp(argv[i], "-s") == 0 && i + 1 < argc) {
s_ssl_cert = argv[++i];
#endif
} else {
print_usage_and_exit(argv[0]);
}
}
/* Open listening socket */
if ((nc = mg_bind(&mgr, s_http_port, ev_handler)) == NULL) {
fprintf(stderr, "mg_bind(%s) failed\n", s_http_port);
exit(EXIT_FAILURE);
}
#if MG_ENABLE_SSL
if (s_ssl_cert != NULL) {
const char *err_str = mg_set_ssl(nc, s_ssl_cert, NULL);
if (err_str != NULL) {
fprintf(stderr, "Error loading SSL cert: %s\n", err_str);
exit(1);
}
}
#endif
mg_set_protocol_http_websocket(nc);
if (s_num_vhost_backends + s_num_default_backends == 0) {
print_usage_and_exit(argv[0]);
}
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
/* Run event loop until signal is received */
printf("Starting LB on port %s\n", s_http_port);
while (s_sig_num == 0) {
mg_mgr_poll(&mgr, 1000);
}
/* Cleanup */
mg_mgr_free(&mgr);
printf("Exiting on signal %d\n", s_sig_num);
return EXIT_SUCCESS;
}
+21
View File
@@ -0,0 +1,21 @@
#!/bin/sh
PROG=$1
PORT=8002
cleanup() {
kill -9 $PID >/dev/null 2>&1
}
#set -x
trap cleanup EXIT
cleanup
$PROG -p $PORT -b /api/ 127.0.0.1:8000 &
PID=$!
# Perform api_server unit test through the load balancer by passing
# load balancer port to the unit test script
(cd ../api_server && make && sh unit_test.sh ./api_server $PORT)
exit $?
-12
View File
@@ -1,12 +0,0 @@
# 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
Loaded 100 of 179 files, more files were not shown because too many files have changed in this diff. Show more