Ketchup 0.2.1 → 0.2.2
raw patch · 102 files changed
+250/−90 lines, 102 filesdep +regex-pcre-builtindep −regex-posixbinary-addedPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: regex-pcre-builtin
Dependencies removed: regex-posix
API changes (from Hackage documentation)
Files
- .git/COMMIT_EDITMSG +1/−1
- .git/FETCH_HEAD +1/−1
- .git/ORIG_HEAD +1/−1
- .git/config +1/−1
- .git/hooks/pre-rebase.sample +1/−1
- .git/hooks/prepare-commit-msg.sample +2/−2
- .git/index binary
- .git/logs/HEAD +17/−4
- .git/logs/refs/heads/master +17/−4
- .git/logs/refs/remotes/origin/HEAD +0/−1
- .git/logs/refs/remotes/origin/master +16/−3
- .git/objects/02/ffa07f6990f74dafd8c9ce6d8fa39b0976d598 binary
- .git/objects/0e/1f7af50ca1ce3a4849668a919daaa5e8735d43 binary
- .git/objects/10/93f2858a4572a7a141c96f251f273da4d55c89 binary
- .git/objects/11/c683f96aaed8fc5cad03cb58c5d1a5cec5f501 binary
- .git/objects/1f/e6614e7ded45533c17b26bdb0d4d4f48cc0e79 binary
- .git/objects/23/8fd86cd3c476d75eec0d7ae736adb96dd1cc60 binary
- .git/objects/24/e1f1b0b0f51b252eebca5b168a4e17b4700a4c binary
- .git/objects/2c/b647573d63d84667f629ea1e26e53bd3f16e44 binary
- .git/objects/2d/d01b33323df8fd6ac686cc492d77822e040e1f binary
- .git/objects/32/4d07aa12666c2f03139f626361d1a810c45bc1 +1/−0
- .git/objects/35/3e50d70c86a2ab186edac1c205561befde3dc7 binary
- .git/objects/36/d1bfaedb400023ee2b6ced7d3847183c454838 +2/−0
- .git/objects/3a/e244621eb9dd53cbcca1a8475acf7affae786d binary
- .git/objects/3c/4e5274662ecf122b692a4b2d4b6054e6ecab6b binary
- .git/objects/3e/c999f52b623f8b354b6f9f7fdee90db24c3c72 +3/−0
- .git/objects/45/3bd93c26a9e271a06ba2646d2fed80284a3d7e binary
- .git/objects/45/fa42399476a801f23b96599909ab8939757cd4 binary
- .git/objects/49/c47bca8a7edae0d0851b71284aa6b39a400cc4 binary
- .git/objects/52/36d892553250d98e5b5b322321702f74559556 binary
- .git/objects/56/2c5d11641eeb9f7ae77cd0d177fd8c947ed1e9 binary
- .git/objects/57/9bdddcdb0584c78cee8df569174d8e5174a28e binary
- .git/objects/58/4b0ea086479c87718b10715c5375a4715e6965 binary
- .git/objects/5c/ad4e38d5d9795fd867fe17bdc34c18ac26dfc6 binary
- .git/objects/62/2888f76a4bd37398710e1590463719c2129297 +2/−0
- .git/objects/63/3f2583dc912547dc4bbb06de8bd1a7c275fcf2 binary
- .git/objects/65/31a477b445bfd988c03147cd43c5a0e289b426 binary
- .git/objects/66/5fba0e7ceccafda2b2a4e92e342ff2cbfe8247 +2/−0
- .git/objects/69/367db2a6835d21762b09eec97395dd0f1d503e binary
- .git/objects/69/8a48637c6765d29ae84a3e63bf4e878af2ae6d +2/−0
- .git/objects/6d/d1040ce0fbc93348725498c912f0e19175d433 binary
- .git/objects/70/8b4d9119f776f28cc85a2a0f58b4bf1c1e7b43 +2/−0
- .git/objects/71/8178fa1b3fc34b7d7d961792c09b4b7b531b3e binary
- .git/objects/72/2f61d1774f7fbd456a5ab611c54408e61f2cc6 binary
- .git/objects/77/a5e6a3a94cbd5f7facc47bed8c940ab8ad3f5c binary
- .git/objects/7a/e848a180a7f100d45ab5ac2437fc0adde1f80e binary
- .git/objects/7b/47d4cbaad3be6b78ecb8c575ffc37bd46a5e5b binary
- .git/objects/7c/e9930fef6b52b551fff42c7b10fde4d08baa0a binary
- .git/objects/7e/51503aee21f2d28f8a91d72e4c081cb7b399e6 binary
- .git/objects/81/ef35d79e1603434714aaa57410da0fa9f71e6e binary
- .git/objects/85/2ae7db61bff45b972b9a21023e162f5003d74d +1/−0
- .git/objects/85/d7f3da362229792ee03571c10da871b205a69f binary
- .git/objects/85/df8a78c3966d87d24b5e8eac0a2bdcbf243c47 binary
- .git/objects/89/2ea0f9d3ad0fefdbacb4e06c7629c1d4317bad binary
- .git/objects/8a/4e26e09c96d9bd95bbefa4c2cde4fbf8087382 +1/−0
- .git/objects/91/e943bb62869a6178db375f7cb03e274ee28ca5 binary
- .git/objects/93/ac39e13883ec31ab749f768d790b6b14a0193d binary
- .git/objects/99/ea1f98242bf7bd4527f7137c29cecb1dc6964a binary
- .git/objects/9c/54e55cc84dfa3ff2649648983bc83143370b10 binary
- .git/objects/a0/6d1fbe75dfe3b2ebe7e03bed4f97c8b05468e3 binary
- .git/objects/a4/52888e3b29d25f2f4b06e3f1cae1ddba210add +1/−0
- .git/objects/a8/f041ca842f9d4b30406cf727910b74b3cc92d6 +3/−0
- .git/objects/ab/8f12f061e87b2922d0371d0c9e971ca49c8de3 binary
- .git/objects/ae/3bc1e76696168e39e4cf60afc4131175005b11 binary
- .git/objects/ae/599a4b87624927da288aa5cd12b2b47b0c7e2d binary
- .git/objects/ae/d160a086ca22c96b3918eb9525818ab4adf8f8 binary
- .git/objects/b1/97449e22aa6e00dcbb765294c13b957ad35207 +3/−0
- .git/objects/b4/73be4e130f0ee35eb42d4037b2c99ee5003326 +1/−0
- .git/objects/b6/3279c85b76f94cc28de65c1a8c2f65c1814e7e binary
- .git/objects/bf/ed8952d1f03e073bc0189e9bcc1371850662ce binary
- .git/objects/c0/9136db3f6d1b79503253d341373405dd2f0e13 binary
- .git/objects/c1/ef5202b4e0351ba9c95229c0edf7dfbfb9bb33 +2/−0
- .git/objects/c6/56325a56d86c044bb5136b9e8dafa421c4f758 binary
- .git/objects/cf/34a7265591f7c3f2d241bd7a554e96c85c68d5 binary
- .git/objects/cf/ed5d601e7c6661a4c95c652d865834a2960cc4 +1/−0
- .git/objects/d2/5e09437126d6267b441999d66ed90f352e67bd binary
- .git/objects/d7/52cbde2090cf51b382e0060e0d13a90ef9dfa8 +1/−0
- .git/objects/d9/50655d5180bc080ff323a21ce6d2c7e369a82b binary
- .git/objects/de/16c2da1cc71609774e151a4e6034c07965db8c binary
- .git/objects/df/0eddb596d9d9aff78156e3cae6300250de9155 binary
- .git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 binary
- .git/objects/e8/7a4422a7db2bee721ce1a88d5286ca1570d36a binary
- .git/objects/ea/395fce07d46082dadd0432b33875394c27e62d +3/−0
- .git/objects/ed/eeef90bd35a9192643cb4fd5f6e108171fadb4 binary
- .git/objects/f3/073c846e4efaa3a1997750b01c078c36a14718 binary
- .git/objects/f6/2e75a62487a4b516a960e045e193098208502c binary
- .git/objects/fb/f8eefabf8c61b21569f198ed873cc8558acd68 +3/−0
- .git/objects/fe/46fa3927ab0bca60de756912a6d427b030a784 binary
- .git/objects/pack/pack-5ae4b09f7084bd1231ef67774ae0cc24689625e4.idx binary
- .git/objects/pack/pack-5ae4b09f7084bd1231ef67774ae0cc24689625e4.pack binary
- .git/packed-refs +0/−2
- .git/refs/heads/master +1/−1
- .git/refs/remotes/origin/HEAD +0/−1
- .git/refs/remotes/origin/master +1/−1
- Ketchup.cabal +27/−9
- Ketchup/Chunked.hs +16/−4
- Ketchup/Httpd.hs +43/−22
- Ketchup/Routing.hs +7/−5
- Ketchup/Static.hs +6/−5
- Ketchup/Utils.hs +44/−16
- README.md +2/−1
- example.hs +12/−4
@@ -1,1 +1,1 @@-Static file handling+0.2.2 - POST DATA
@@ -1,1 +1,1 @@-2e95147d7a2c86bb224527a5624c118b53f9c736 branch 'master' of github.com:Hamcha/Ketchup+a06d1fbe75dfe3b2ebe7e03bed4f97c8b05468e3 branch 'master' of github.com:Hamcha/ketchup
@@ -1,1 +1,1 @@-6dd1040ce0fbc93348725498c912f0e19175d433+c09136db3f6d1b79503253d341373405dd2f0e13
@@ -4,7 +4,7 @@ bare = false logallrefupdates = true [remote "origin"]- url = git@github.com:Hamcha/Ketchup.git+ url = git@github.com:Hamcha/ketchup.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "master"] remote = origin
@@ -65,7 +65,7 @@ fi else not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`- /usr/bin/perl -e '+ /usr/local/bin/perl -e ' my $topic = $ARGV[0]; my $msg = "* $topic has commits already merged to public branch:\n"; my (%not_in_next) = map {
@@ -22,10 +22,10 @@ case "$2,$3" in merge,)- /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;+ /usr/local/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; # ,|template,)-# /usr/bin/perl -i.bak -pe '+# /usr/local/bin/perl -i.bak -pe ' # print "\n" . `git diff --cached --name-status -r` # if /^#/ && $first++ == 0' "$1" ;;
binary file changed (976 → 976 bytes)
@@ -1,4 +1,17 @@-0000000000000000000000000000000000000000 6dd1040ce0fbc93348725498c912f0e19175d433 hamcha <hamcha@localhost.localdomain> 1400936747 +0200 clone: from git@github.com:Hamcha/Ketchup.git-6dd1040ce0fbc93348725498c912f0e19175d433 2e95147d7a2c86bb224527a5624c118b53f9c736 hamcha <hamcha@localhost.localdomain> 1400972318 +0200 pull: Fast-forward-2e95147d7a2c86bb224527a5624c118b53f9c736 41bb1c25a293d0754c3554f5a07a8a2a883b40d8 Hamcha <zikyky@gmail.com> 1401224802 +0200 commit: Regexp matching-41bb1c25a293d0754c3554f5a07a8a2a883b40d8 73e675e209d1125be27d5c119389c9337942f3b9 Hamcha <zikyky@gmail.com> 1401228393 +0200 commit: Static file handling+0000000000000000000000000000000000000000 c1ef5202b4e0351ba9c95229c0edf7dfbfb9bb33 Hamcha <zikyky@gmail.com> 1400868061 +0200 commit (initial): Base commit (most code from httpd)+c1ef5202b4e0351ba9c95229c0edf7dfbfb9bb33 b197449e22aa6e00dcbb765294c13b957ad35207 Hamcha <zikyky@gmail.com> 1400868225 +0200 commit: Took some useless stuff out+b197449e22aa6e00dcbb765294c13b957ad35207 622888f76a4bd37398710e1590463719c2129297 Hamcha <zikyky@gmail.com> 1400868258 +0200 commit: .gitignore to avoid commiting binaries or object files+622888f76a4bd37398710e1590463719c2129297 d752cbde2090cf51b382e0060e0d13a90ef9dfa8 Hamcha <zikyky@gmail.com> 1400871180 +0200 commit: Basic routing should work+d752cbde2090cf51b382e0060e0d13a90ef9dfa8 708b4d9119f776f28cc85a2a0f58b4bf1c1e7b43 Hamcha <zikyky@gmail.com> 1400871591 +0200 commit: Added types+708b4d9119f776f28cc85a2a0f58b4bf1c1e7b43 722f61d1774f7fbd456a5ab611c54408e61f2cc6 Hamcha <zikyky@gmail.com> 1400872235 +0200 commit: Replaced incorrect 400 errors with 404 ones+722f61d1774f7fbd456a5ab611c54408e61f2cc6 1093f2858a4572a7a141c96f251f273da4d55c89 Hamcha <zikyky@gmail.com> 1400884141 +0200 commit: Removed some oleg sauce+1093f2858a4572a7a141c96f251f273da4d55c89 cf34a7265591f7c3f2d241bd7a554e96c85c68d5 Hamcha <zikyky@gmail.com> 1400935032 +0200 commit: Hackage stuff+cf34a7265591f7c3f2d241bd7a554e96c85c68d5 6dd1040ce0fbc93348725498c912f0e19175d433 Hamcha <zikyky@gmail.com> 1400936077 +0200 commit: README+6dd1040ce0fbc93348725498c912f0e19175d433 691816444999b3c2b424bdcef7eeb977fa8b18a5 Hamcha <zikyky@gmail.com> 1400953254 +0200 commit: Chunked encoding+691816444999b3c2b424bdcef7eeb977fa8b18a5 ab41fa19ef0fbeb0088d8f325b1dc4280cbe9817 Hamcha <zikyky@gmail.com> 1400953623 +0200 commit: Forgot to make it visible+ab41fa19ef0fbeb0088d8f325b1dc4280cbe9817 2e95147d7a2c86bb224527a5624c118b53f9c736 Hamcha <zikyky@gmail.com> 1400960297 +0200 commit: Removed SERVER header and set to 0.1.1+2e95147d7a2c86bb224527a5624c118b53f9c736 718178fa1b3fc34b7d7d961792c09b4b7b531b3e Hamcha <zikyky@gmail.com> 1401303609 +0200 pull: Fast-forward+718178fa1b3fc34b7d7d961792c09b4b7b531b3e b473be4e130f0ee35eb42d4037b2c99ee5003326 Hamcha <zikyky@gmail.com> 1401319907 +0200 commit: Ketchup has been Haddock'ed+b473be4e130f0ee35eb42d4037b2c99ee5003326 c09136db3f6d1b79503253d341373405dd2f0e13 Hamcha <zikyky@gmail.com> 1401541834 +0200 commit: Switch to PCRE regexps+c09136db3f6d1b79503253d341373405dd2f0e13 a06d1fbe75dfe3b2ebe7e03bed4f97c8b05468e3 Hamcha <zikyky@gmail.com> 1401998815 +0200 pull: Fast-forward+a06d1fbe75dfe3b2ebe7e03bed4f97c8b05468e3 a8f041ca842f9d4b30406cf727910b74b3cc92d6 Hamcha <zikyky@gmail.com> 1402149852 +0200 commit: 0.2.2 - POST DATA
@@ -1,4 +1,17 @@-0000000000000000000000000000000000000000 6dd1040ce0fbc93348725498c912f0e19175d433 hamcha <hamcha@localhost.localdomain> 1400936747 +0200 clone: from git@github.com:Hamcha/Ketchup.git-6dd1040ce0fbc93348725498c912f0e19175d433 2e95147d7a2c86bb224527a5624c118b53f9c736 hamcha <hamcha@localhost.localdomain> 1400972318 +0200 pull: Fast-forward-2e95147d7a2c86bb224527a5624c118b53f9c736 41bb1c25a293d0754c3554f5a07a8a2a883b40d8 Hamcha <zikyky@gmail.com> 1401224802 +0200 commit: Regexp matching-41bb1c25a293d0754c3554f5a07a8a2a883b40d8 73e675e209d1125be27d5c119389c9337942f3b9 Hamcha <zikyky@gmail.com> 1401228393 +0200 commit: Static file handling+0000000000000000000000000000000000000000 c1ef5202b4e0351ba9c95229c0edf7dfbfb9bb33 Hamcha <zikyky@gmail.com> 1400868061 +0200 commit (initial): Base commit (most code from httpd)+c1ef5202b4e0351ba9c95229c0edf7dfbfb9bb33 b197449e22aa6e00dcbb765294c13b957ad35207 Hamcha <zikyky@gmail.com> 1400868225 +0200 commit: Took some useless stuff out+b197449e22aa6e00dcbb765294c13b957ad35207 622888f76a4bd37398710e1590463719c2129297 Hamcha <zikyky@gmail.com> 1400868258 +0200 commit: .gitignore to avoid commiting binaries or object files+622888f76a4bd37398710e1590463719c2129297 d752cbde2090cf51b382e0060e0d13a90ef9dfa8 Hamcha <zikyky@gmail.com> 1400871180 +0200 commit: Basic routing should work+d752cbde2090cf51b382e0060e0d13a90ef9dfa8 708b4d9119f776f28cc85a2a0f58b4bf1c1e7b43 Hamcha <zikyky@gmail.com> 1400871591 +0200 commit: Added types+708b4d9119f776f28cc85a2a0f58b4bf1c1e7b43 722f61d1774f7fbd456a5ab611c54408e61f2cc6 Hamcha <zikyky@gmail.com> 1400872235 +0200 commit: Replaced incorrect 400 errors with 404 ones+722f61d1774f7fbd456a5ab611c54408e61f2cc6 1093f2858a4572a7a141c96f251f273da4d55c89 Hamcha <zikyky@gmail.com> 1400884141 +0200 commit: Removed some oleg sauce+1093f2858a4572a7a141c96f251f273da4d55c89 cf34a7265591f7c3f2d241bd7a554e96c85c68d5 Hamcha <zikyky@gmail.com> 1400935032 +0200 commit: Hackage stuff+cf34a7265591f7c3f2d241bd7a554e96c85c68d5 6dd1040ce0fbc93348725498c912f0e19175d433 Hamcha <zikyky@gmail.com> 1400936077 +0200 commit: README+6dd1040ce0fbc93348725498c912f0e19175d433 691816444999b3c2b424bdcef7eeb977fa8b18a5 Hamcha <zikyky@gmail.com> 1400953254 +0200 commit: Chunked encoding+691816444999b3c2b424bdcef7eeb977fa8b18a5 ab41fa19ef0fbeb0088d8f325b1dc4280cbe9817 Hamcha <zikyky@gmail.com> 1400953623 +0200 commit: Forgot to make it visible+ab41fa19ef0fbeb0088d8f325b1dc4280cbe9817 2e95147d7a2c86bb224527a5624c118b53f9c736 Hamcha <zikyky@gmail.com> 1400960297 +0200 commit: Removed SERVER header and set to 0.1.1+2e95147d7a2c86bb224527a5624c118b53f9c736 718178fa1b3fc34b7d7d961792c09b4b7b531b3e Hamcha <zikyky@gmail.com> 1401303609 +0200 pull: Fast-forward+718178fa1b3fc34b7d7d961792c09b4b7b531b3e b473be4e130f0ee35eb42d4037b2c99ee5003326 Hamcha <zikyky@gmail.com> 1401319907 +0200 commit: Ketchup has been Haddock'ed+b473be4e130f0ee35eb42d4037b2c99ee5003326 c09136db3f6d1b79503253d341373405dd2f0e13 Hamcha <zikyky@gmail.com> 1401541834 +0200 commit: Switch to PCRE regexps+c09136db3f6d1b79503253d341373405dd2f0e13 a06d1fbe75dfe3b2ebe7e03bed4f97c8b05468e3 Hamcha <zikyky@gmail.com> 1401998815 +0200 pull: Fast-forward+a06d1fbe75dfe3b2ebe7e03bed4f97c8b05468e3 a8f041ca842f9d4b30406cf727910b74b3cc92d6 Hamcha <zikyky@gmail.com> 1402149852 +0200 commit: 0.2.2 - POST DATA
@@ -1,1 +0,0 @@-0000000000000000000000000000000000000000 6dd1040ce0fbc93348725498c912f0e19175d433 hamcha <hamcha@localhost.localdomain> 1400936747 +0200 clone: from git@github.com:Hamcha/Ketchup.git
@@ -1,3 +1,16 @@-6dd1040ce0fbc93348725498c912f0e19175d433 2e95147d7a2c86bb224527a5624c118b53f9c736 hamcha <hamcha@localhost.localdomain> 1400972318 +0200 pull: fast-forward-2e95147d7a2c86bb224527a5624c118b53f9c736 41bb1c25a293d0754c3554f5a07a8a2a883b40d8 Hamcha <zikyky@gmail.com> 1401224806 +0200 update by push-41bb1c25a293d0754c3554f5a07a8a2a883b40d8 73e675e209d1125be27d5c119389c9337942f3b9 Hamcha <zikyky@gmail.com> 1401228397 +0200 update by push+0000000000000000000000000000000000000000 c1ef5202b4e0351ba9c95229c0edf7dfbfb9bb33 Hamcha <zikyky@gmail.com> 1400868072 +0200 update by push+c1ef5202b4e0351ba9c95229c0edf7dfbfb9bb33 622888f76a4bd37398710e1590463719c2129297 Hamcha <zikyky@gmail.com> 1400868261 +0200 update by push+622888f76a4bd37398710e1590463719c2129297 d752cbde2090cf51b382e0060e0d13a90ef9dfa8 Hamcha <zikyky@gmail.com> 1400871184 +0200 update by push+d752cbde2090cf51b382e0060e0d13a90ef9dfa8 708b4d9119f776f28cc85a2a0f58b4bf1c1e7b43 Hamcha <zikyky@gmail.com> 1400871595 +0200 update by push+708b4d9119f776f28cc85a2a0f58b4bf1c1e7b43 722f61d1774f7fbd456a5ab611c54408e61f2cc6 Hamcha <zikyky@gmail.com> 1400872238 +0200 update by push+722f61d1774f7fbd456a5ab611c54408e61f2cc6 1093f2858a4572a7a141c96f251f273da4d55c89 Hamcha <zikyky@gmail.com> 1400884145 +0200 update by push+1093f2858a4572a7a141c96f251f273da4d55c89 cf34a7265591f7c3f2d241bd7a554e96c85c68d5 Hamcha <zikyky@gmail.com> 1400935037 +0200 update by push+cf34a7265591f7c3f2d241bd7a554e96c85c68d5 6dd1040ce0fbc93348725498c912f0e19175d433 Hamcha <zikyky@gmail.com> 1400936080 +0200 update by push+6dd1040ce0fbc93348725498c912f0e19175d433 691816444999b3c2b424bdcef7eeb977fa8b18a5 Hamcha <zikyky@gmail.com> 1400953258 +0200 update by push+691816444999b3c2b424bdcef7eeb977fa8b18a5 ab41fa19ef0fbeb0088d8f325b1dc4280cbe9817 Hamcha <zikyky@gmail.com> 1400953627 +0200 update by push+ab41fa19ef0fbeb0088d8f325b1dc4280cbe9817 2e95147d7a2c86bb224527a5624c118b53f9c736 Hamcha <zikyky@gmail.com> 1400960301 +0200 update by push+2e95147d7a2c86bb224527a5624c118b53f9c736 718178fa1b3fc34b7d7d961792c09b4b7b531b3e Hamcha <zikyky@gmail.com> 1401303609 +0200 pull: fast-forward+718178fa1b3fc34b7d7d961792c09b4b7b531b3e b473be4e130f0ee35eb42d4037b2c99ee5003326 Hamcha <zikyky@gmail.com> 1401319911 +0200 update by push+b473be4e130f0ee35eb42d4037b2c99ee5003326 c09136db3f6d1b79503253d341373405dd2f0e13 Hamcha <zikyky@gmail.com> 1401541838 +0200 update by push+c09136db3f6d1b79503253d341373405dd2f0e13 a06d1fbe75dfe3b2ebe7e03bed4f97c8b05468e3 Hamcha <zikyky@gmail.com> 1401998815 +0200 pull: fast-forward+a06d1fbe75dfe3b2ebe7e03bed4f97c8b05468e3 a8f041ca842f9d4b30406cf727910b74b3cc92d6 Hamcha <zikyky@gmail.com> 1402149856 +0200 update by push
binary file changed (absent → 34 bytes)
binary file changed (absent → 1258 bytes)
binary file changed (absent → 167 bytes)
binary file changed (absent → 1791 bytes)
binary file changed (absent → 478 bytes)
binary file changed (absent → 1770 bytes)
binary file changed (absent → 115 bytes)
binary file changed (absent → 116 bytes)
binary file changed (absent → 40 bytes)
@@ -0,0 +1,1 @@+xÅRÁn1Í:»í¡AÑ Ò(¨)4"ôPE²«zmÇ7 (ÿÞ1¹¥×ø`Ù~3OïÍóJÛ\|ÎO{§ð}4»ù9º¹Ñk+ª{òÙ8í½´[íVmUÔSYx*Ñcz¬jg=Ácºú]¡¯d¼#lºA¿U6.áÓW^×-Òº®?!rê?øÜFbIJ)b}¥Qàñô²ðm·o@£æèôn_ò)ÏááL\YCh¨·Ø9]x[ÊJªµXvL5&ët<Ì?î¯YKâæbÍ\è¿lL"â«!SG¯» l5,²CX6¼1OôUjJV6Þ×Iùq8©uof²F0¼±EÆÐQù<ûÁ¶Ã6¶§#2³Tr@ÀkvµA45ß§}míè@Ä^Ksð.z.ÙR^+jît¯ûIeûFÖæÖá©òoMß4 ºâÌd±¸q.`òÏÿ°mé
binary file changed (absent → 67 bytes)
@@ -0,0 +1,2 @@+x¥TÁn@íxr"$¶RUU¨¸MR5vªÄU³.(K%¶[÷ß;kÔHå`Ïî̼y3oà^È{¼~óêíÃ\M/¿]~ÄÍWB²wZEÉ÷'Ã_¶e[±rÁñëe§ÞÌ5¹mË"ÛuȶũT9Ñ*â>0ͼóæ¨7zaÜ<aiáÔ~Ô¡p¥uñO¹^KõP)©a4ÂÜ7êàÜÉå×¾ÇÕlöå?æ<+ÎÄ+j7Ã[·ÈùtÇuÝ m¬è¦°5\æº%MTíkûÈxL¥¾y ò4GÊ^2wï0ibFºÀÉUd<.µ¤\øp2B$ë ä)R¦XÏ3ÐR®ë(ã5ó?_©_ýføåÚ¼HÖ@i`çs)mY+ÍãT0ÍáW<hX8Å(ýéKu¤AÇiiTÁûM,xÈ ¶¯¸ÜaßǶ°ÛÊiÍíRa>ö\lD»´6êY+tì KØýQß`Pè_ 3mÖÍùT6KE¤ÑÙ/z®ø ¿é7+Æ¿×÷óÿÇÚ×PÇ´x+%ãëÖí«Hh®à$RÃ3óò@»è6T>/üé9+Ö¡vãôz^ÏmªØ- }4Jq7®½§×©ÿ*ý¡X,
binary file changed (absent → 589 bytes)
binary file changed (absent → 660 bytes)
@@ -0,0 +1,3 @@+x¥T[oÓ0æ¹RÿÃQ7©´xAP)H[Ù±nS×iH^s²XsâÌvh+ ßÎq2gM×Â$ò'çâïrì[!oáí»o^ýìÀéþÙñÕþñ!G%$0º4gwv¿ºn'Q!¾ %ELda(Üíx hÝó-ºæRx(à1Ç>1à Áºi0JzLÃh[òå6>nâÐ<Â1yôøáBo¡KußD`N±4Áï°.FC`bù°´QA\d3ÃeVý²{ÔÀ@pm@Ƶô#S235 ésÐS¥$ ð¡ÀBn§C¸öF+íw)g÷h`ðN¦Ó°ðÆUjµ°õáÛêÏçàù¾ÄãTDHV[=Õ:×7MUB,i*Ä>Yt&Í,~ueV,µÿpe5%¤¦+¼BqñÁ©ò©³§©#Ö¶Ü]áÐÀÚ¼Èb©Þ¼Ã É!5ç7´kkÁm Õé¨-'Hûõï)¦*½P¦¹ A°Æagg~ðü+7 ÌdJÐ(Þ¦)Í«Eüxà,`YÂâI%°xÎ(©*謺Èh2¯GAV=]Êÿ ÝJ ²ÚRïþ°ïzPêzÓ&Aøû9çÛÔomßÈ@n ÿºoÕ©8uÉjÜilz8õÕ´Úäâ8LOíµ°1öæð2i pÊ@ãíÃÊ;ÏÒÅKÄLoR×\ßî¥×ëíõzþªñÛݤûÈ0^ÍCÙ*ÙnV«ÿÛõ¸ã
binary file changed (absent → 122 bytes)
binary file changed (absent → 156 bytes)
binary file changed (absent → 505 bytes)
binary file changed (absent → 281 bytes)
binary file changed (absent → 116 bytes)
binary file changed (absent → 1050 bytes)
binary file changed (absent → 156 bytes)
binary file changed (absent → 116 bytes)
@@ -0,0 +1,2 @@+xMJ1F]çµêt~AĥרTjzÊt$ ñô6x·ï}<îéë×§9DÏR} ¸HäÂB³çàmMÁ§ÕÍù¤!û²äè\k V.%óìxYKöêê-FC_óÒ¼SãÁË^×ÇÛÖHo'îíB²>Á3ZDsÐ#mÊ¿$sÚtê¶÷!0;Ðw×+OºoPt§¡r#¥á g½ÉÝü|7Q,
binary file changed (absent → 1874 bytes)
binary file changed (absent → 1473 bytes)
@@ -0,0 +1,2 @@+x+)JMU041c040031QÐKÏ,ÉLÏË/Je`ú¿ >sÂwßõ7NËí_<³ìê PðN-IÎ(-`¼Æò¡EóÃÕ%óËz+¹³EHÚBÍñMÌNMËÌIeh|oz}³³»Èª¥%·øW~˪IHÌ-ÈIÕË(f0òe_%£Ï,<?)9ñâ+#ÑÍ:
binary file changed (absent → 1759 bytes)
@@ -0,0 +1,2 @@+xußOã0Çï¹ 5h¥µÇN: !Äøy:mO¡qi´¬)L§ûßÏIµG_Û_;ö'~ÖæoÒ=¸=»»]_ÂýZmD9!«ª{éß(ZÙhßHyÙÔÙ©<ÁµÞK´EjYKðÚ+ .l´"lËe祰G ¾ÐEíÃãu6_(5ÅÊ.«ÜHnî³fÝà Q-¿ÏHi÷9|ônìâË@6VKü¬á2»Ps2v±4è®ÆËå(¢4 ì RTR£Zzp|£#HOabò?ÝL§øÚ #6,|$gÖNj×H¼è×=ÄÉú¢ÂhÊJÅW¨ KC&TÖ|c-¨dÏ(ËMyÓÆ*ô´Q:Q!+ý//1_ÔmìúTSÕ¢÷t8Iùî*Þ§ÊäÂ!0)û;úAÒÀAáqLmà'e ôÔwªlñéôÆJ>bWÃÃCǽsSVNW5öúó%¿óSÒκS ]ó7¼ÞsÄB4üܳhjØgï*Ψ8¢,Ût¾©Òú ¿ñ>âj¾lzñÐLGõ#ܺ2 Óá×¢í+ügFÆè¢±{ mxºáñ\0·ÓµNÍyIL°[Ç5V/¼11ï5ñjÅÃ!ô²¬@0GÀ BíÖæîxÜJô¤ä\
binary file changed (absent → 158 bytes)
@@ -0,0 +1,2 @@+xÎM+Â0@a×9Åìü5Ñ×&[jl©qQOo¯àöÁ/͵ ÆC[EÀùÂÎX":¨±=u¸d)ø²S¯òj7©Ïb0¯{ v(µeB)GÅ6Ì+ܹ¦áü§mÚ®ÊãóæzícÐ4Ñ ª½îkMþBê³dhÛ"oõÌP@Ú
binary file changed (absent → 185 bytes)
binary file changed (absent → 181 bytes)
binary file changed (absent → 931 bytes)
binary file changed (absent → 1063 bytes)
binary file changed (absent → 1333 bytes)
binary file changed (absent → 581 bytes)
binary file changed (absent → 178 bytes)
binary file changed (absent → 82 bytes)
@@ -0,0 +1,1 @@+x ±jÃ0;ü?dIi H!vKv¡³b"m9'¹¥¾{%7vª¶Ó}Ü}ÿ[Ärµºù3<mwíî/oÄWô>²ëO3ñUeÑy=¶Gy® e1G¢º²XàÝS]7x8ªuÆÆ½JV~&BTÿc²¶o3\_a\ß43»°TC¼[d1±o¢ó=çɰKQ¢2þ¥Së5ªßâîO}¡600eU¦ÌécZ2¥ËJÍZjöëué\¹okßsð
binary file changed (absent → 281 bytes)
binary file changed (absent → 248 bytes)
binary file changed (absent → 280 bytes)
@@ -0,0 +1,1 @@+xKÊÉOR0·`HÌɱRHHÌ-ÈIååâå2x¹8Ó3a2zÅ ÉäÔÄ<TQ®^¾>ÔËÈ332Ißå
binary file changed (absent → 616 bytes)
binary file changed (absent → 116 bytes)
binary file changed (absent → 178 bytes)
binary file changed (absent → 178 bytes)
binary file changed (absent → 169 bytes)
@@ -0,0 +1,1 @@+xEÐAKÃ0`Ïþ좲vUAv¨ UeÎÒæÓ²¤K¿¨Cüï&ÛÔBÞäÉÔÊÔpv~rðávz7{ήáþ2\ x"+õ[Ãø;DN!,¸ÔðѢŰ(W±ÿã©i]Ì:ñ¯WòU¢+N<)6;xEZ® ×[-Àâ.@hصxÄNm¶ÙiByÈ.&Ô/7²QÉ?iÜÒJ±ê¨ò@ßÝûØw ÓßÀóEÒÝpe§Ðæ/Æ »öäfuÎFÎªËÆu÷XµS}âge8²ýð/¡6(é=_.3¤vÏû}«q1
@@ -0,0 +1,3 @@+xA+Â0E]ç³ËdÒ¤ XpáNA/¤[j¬¸¨§·Wp÷y¼?N9HM!$î¬ÓÔɱQ!¢´]QªFZÆPdñö3¿+x4«¸Ñ]b×ɨwurM´um,+á?¥f8û{ûï0.ãr|d?<«8åÈIÖÎj-¢Xézð_À*\/·;Ú{+~rB4
binary file changed (absent → 580 bytes)
binary file changed (absent → 280 bytes)
binary file changed (absent → 614 bytes)
binary file changed (absent → 281 bytes)
@@ -0,0 +1,3 @@+xÎM+Â0@a×9ÅìN~ÚKà&ÉDK&]èéõ+n|ðb-eî@äv}cuH^Grì ÆÑ&g\¢,iBë4zñ*Ïql )AmÀ>zKä#JÊcÊ!Ö·~¯+\¸Ä;Ãñ3/ïå}¾XË 8¹È QýêoË_H]k] Õ"°5yHkÐú3Ô«/THg
@@ -0,0 +1,1 @@+xAnÃ0sÖ+xË¡@AJ®eAc¾"©Øp®rH^_}¡ÇÝÁbV¶ZÞ¶Á¼1¤Í,y0%> é(fV÷àÝî "M§ÂC0ä¨QÓH1yÁ{Î_¡3süló¶Ã «Ì§÷²¾Ö×ùZy¹}ÊV¿¤@)aô®·ýZ³Ü5ù²Ù½U7Y¦în¸Gõ
binary file changed (absent → 156 bytes)
binary file changed (absent → 281 bytes)
binary file changed (absent → 169 bytes)
@@ -0,0 +1,2 @@+xA+Â0E]ç³T1i¸òcÚRH;.êé xïÃã¿TDFÍFç¡·lÎg{fËÔ¶!4xGJb²Éß:n,i`8}ÆiÖËCx|R3C>¢'ØãÑÔµ^iþK2W^2üTØJY´BW;ç"0¨¾ºù¢;U
binary file changed (absent → 557 bytes)
binary file changed (absent → 158 bytes)
@@ -0,0 +1,1 @@+x+)JMU041c040031QÐKÏ,ÉLÏË/Je`ú¿ >sÂwßõ7NËí_<³ìê PðN-IÎ(-`xûîý½¦+%ÕOû_ýöC\~í¨9¾Ù©i9©ïM¯ÏcvvYµ´DàÿÊïryP5©¹9©zÅK:ú¬5/Åë{³=þxêáÝ]\w]>¿
binary file changed (absent → 178 bytes)
@@ -0,0 +1,1 @@+xÎMjÃ0@á®uÙÊh¬èB Yõãñ(¢¢Èôôõº}ðÁVk@äÞFWïOyfÔ *Âya&ÒÉQÎ$sÖH.oîúàb9xvó2 )Å`Qí)¡óS°IÈR¢ïcm¾¸ÊÊpþ-Ûk{]nËýCZýël#Â;¢9ê±6ô_È\ùYzÛGyÜ๶ý¾ÀOëù:©F¤
binary file changed (absent → 896 bytes)
binary file changed (absent → 424 bytes)
binary file changed (absent → 675 bytes)
binary file changed (absent → 15 bytes)
binary file changed (absent → 560 bytes)
@@ -0,0 +1,3 @@+x}SMÓ0å)ÿaN6îP±U»¢BWl÷°]àl;ÓĪcGöDmøõ8in9)RôÞyó¡Ow7O¼À)\H2¯Ê8ú Î+kznÈÆlGK.¸N.äýFlG_*¥Óä¥.»l[UãèQI4þb±yxéÁäÒ-óø°\=mWq´¨(·®÷ Fï¹I ïHÅÑ+CáÅNõGíë}ý9+¸ÒLÚ"Ö¶Àg½eNTúé`)Ê+Ñhk^Èú^0³®>ÿFq=»-½ògv¾*Ñ/¸Öp@;¦x°n;ë ´à¹ Ô·ZíÊ 4;nd Ò¦(¸GGÍ4ã®#Õ±±i²±iZ6+a˼2{L¯ÁuhïèÙV¤LvÛ'%¯±¤t[Ài_±D}+/Ñ~ÍçØ»ÛSQzrÁ©ÝÏìnn`6d£Q§¶Û¿Ò;ݤ¥Ê¡¤° ^0±÷]¨+÷uÉÒ¸;Ú µx³1ûØÑ3<&¥tp©a<mòaGéudó¢Íz¶¶rg«Qm}9òvâM5ç£×ÕLE[Æ|úmô¿³cmÈ_±:ù÷
binary file changed (absent → 116 bytes)
binary file changed (absent → 122 bytes)
binary file changed (absent → 626 bytes)
@@ -0,0 +1,3 @@+x¥SMOÜ0í9RþÃÓ´ié©BÊÒJe[ Zõ8ìm,9qðìÒíg¬Ó %R¥ú<oß¼±o¾ÁÑÑÛ7¯~îáüäËÙ÷³øz/Ò~é¬Zìþ£8ª4÷Jà³p·¥o²í¥ã(¡PÄQR+âHV6w)¹ã=s,;];ÑfEÉÌ;0b+¼`M_y+Høä\ÃÛã:¸º%«9i5âÎëÃ2~±Ýèó¡01ÇM1.£qQÏH¼2E²$bwbkÞFãZì£aUöåº@yö ´ÿ òÞÙþkñw#éty£àDÕ(æòú'²ù!][]BÒßY¸ªé¼[{p@®°îsíæ+ä9Öm<^9¾O»h«"«½RX`®É¦LÙ´ÈJÁ8Ñ÷üx8úéI#Ò¡¹µó×ó¶ç^_èøwóÁ¹ÖÏíÀ^2t-®Î% }K©0Hjí &3ÐHw\7¸ÿÛè3ß§ÝLf³Ù,ݵ~ÚϤÈÝDV£©ÛGzÿ?} ¹Cn
binary file changed (absent → 179 bytes)
binary file changed (2304 → absent bytes)
binary file changed (8124 → absent bytes)
@@ -1,2 +0,0 @@-# pack-refs with: peeled fully-peeled -6dd1040ce0fbc93348725498c912f0e19175d433 refs/remotes/origin/master
@@ -1,1 +1,1 @@-73e675e209d1125be27d5c119389c9337942f3b9+a8f041ca842f9d4b30406cf727910b74b3cc92d6
@@ -1,1 +0,0 @@-ref: refs/remotes/origin/master
@@ -1,1 +1,1 @@-73e675e209d1125be27d5c119389c9337942f3b9+a8f041ca842f9d4b30406cf727910b74b3cc92d6
@@ -1,5 +1,5 @@ Name: Ketchup -Version: 0.2.1 +Version: 0.2.2 Cabal-Version: >= 1.6 Build-Type: Simple License: MIT @@ -9,7 +9,25 @@ Homepage: https://github.com/Hamcha/Ketchup Category: Web, Ketchup Synopsis: A super small web framework for those who don't like big and fancy codebases +Description: + Ketchup is a very small HTTP server implementation, the HTTPd itself is one file with + around 100 LoC! + . + Ketchup is also made to be as modular and embeddable as possible, if you want a cabal-free Haskell app, just take those two/three files you need and put it in your project! + . + Currently Ketchup comes with: + . + * Basic httpd functionality (IPV4 only for now) + * Regexp routing with parameters (ie. \`/user/:name/(.\*)\`) + * Static file handler (ie. \`static \".\"\` as route/handler) + . + Most of the project is dependency-free and only uses stuff included in Prelude, with the exception of Ketchup.Static who uses the mime-types package from Hackage, which is a single separately downloadable file. +Extra-Source-Files: + example.hs, + LICENSE, + README.md + Library Exposed-Modules: Ketchup.Chunked @@ -19,14 +37,14 @@ Ketchup.Utils Build-Depends: - base ==4.*, - bytestring >=0.9 && <0.11, - containers >=0.2 && <0.6, - directory <1.3, - mime-types <0.2, - network <2.5, - regex-posix <1.0, - text <2.0 + base ==4.*, + bytestring >=0.9 && <0.11, + containers >=0.2 && <0.6, + directory <1.3, + mime-types <0.2, + network <2.5, + regex-pcre-builtin <1.0, + text <2.0 Source-Repository head type: git
@@ -14,21 +14,33 @@ import Network.Socket.ByteString import Numeric -chunkHeaders :: Socket -> Int -> [(B.ByteString, [B.ByteString])] -> IO () +-- |Send HTTP reply headers and begin a Chunked transmission +chunkHeaders :: Socket -- ^ Socket to write to + -> Int -- ^ Status code + -> [(B.ByteString, [B.ByteString])] -- ^ Headers + -> IO () chunkHeaders handle status headers = do sendAll handle content where content = B.concat ["HTTP/1.1 ", statusMsg status, "\r\n\ \Connection: close\r\n",heads,"\r\n\ \Transfer-Encoding: chunked\r\n\r\n"] - heads = B.concat $ map (\x -> B.concat [fst x, ": ", B.concat $ List.intersperse "," $ snd x]) headers + heads = B.concat $ map toHeader headers + toHeader x = B.concat [fst x, ": " + ,B.concat $ List.intersperse "," $ snd x + ,"\r\n"] -chunk :: Socket -> B.ByteString -> IO () +-- |Sends a chunk +chunk :: Socket -- ^ Socket to write to + -> B.ByteString -- ^ Content to write + -> IO () chunk handle value = sendAll handle content where content = B.concat [B.pack $ showHex (B.length value) "", "\r\n", value, "\r\n"] -endchunk :: Socket -> IO () +-- |Send the final/closing chunk +endchunk :: Socket -- ^ Socket to write to + -> IO () endchunk handle = sendAll handle "0\r\n\r\n"
@@ -1,8 +1,9 @@ {-# LANGUAGE OverloadedStrings #-} module Ketchup.Httpd -( HTTPRequest -, method, uri, httpver, headers +( HTTPRequest (..) +, Headers (..) +, Handler (..) , listenHTTP ) where @@ -16,54 +17,71 @@ import Ketchup.Utils import System.IO --- HTTP Request type -data HTTPRequest = HTTPRequest { method :: B.ByteString - , uri :: B.ByteString - , httpver :: B.ByteString - , headers :: Map.Map B.ByteString [B.ByteString] - } deriving (Show) +type Headers = Map.Map B.ByteString [B.ByteString] +type Handler = Socket -> HTTPRequest -> IO () +-- |HTTP Request type +-- This holds a complete HTTP request +data HTTPRequest = HTTPRequest + { method :: B.ByteString -- ^ Get HTTP Request Method (GET, POST etc.) + , uri :: B.ByteString -- ^ Get Request URI + , httpver :: B.ByteString -- ^ Get HTTP Version + , headers :: Headers -- ^ Get HTTP Headers (header, [values]) + , body :: B.ByteString -- ^ Get HTTP Post body (raw string) + } deriving (Show) + -- Parses header lines parseRequestLine :: B.ByteString -> (B.ByteString, [B.ByteString]) parseRequestLine line = (property, values) where property = head items - values = C.split ',' $ (trim . last) items - items = C.split ':' line + values = C.split ',' $ (trim . last) items + items = C.split ':' line +-- Parses request body +parseRequestBody :: B.ByteString -> [(B.ByteString, B.ByteString)] +parseRequestBody body = + map sep items + where + sep = breakBS "=" + items = C.split '&' body + -- Gets all request lines -getRequest :: Socket -> IO [B.ByteString] +getRequest :: Socket -> IO ([B.ByteString], B.ByteString) getRequest client = do content <- recv client 1024 - return $ C.lines content + let (headers, body) = breakBS "\r\n\r\n" content + return (C.lines headers, body) -- Parses requests -parseRequest :: [B.ByteString] -> HTTPRequest +parseRequest :: ([B.ByteString], B.ByteString) -> HTTPRequest parseRequest reqlines = - HTTPRequest { method=met, uri=ur, httpver=ver, headers=heads } + HTTPRequest { method=met, uri=ur, httpver=ver, headers=heads, body=body } where - [met, ur, ver] = (C.words . head) reqlines -- First line is METHOD URI VERSION - heads = Map.fromList $ map parseRequestLine $ tail reqlines + [met, ur, ver] = C.words $ head headers -- First line is METHOD URI VERSION + heads = Map.fromList $ map parseRequestLine $ tail headers + body = snd reqlines + headers = fst reqlines -- Handles each client request -handleRequest :: Socket -> (Socket -> HTTPRequest -> IO ()) -> IO () +handleRequest :: Socket -> Handler -> IO () handleRequest client cback = do reqlines <- getRequest client - case length reqlines of + case length (fst reqlines) of 0 -> sendBadRequest client _ -> cback client $ parseRequest reqlines sClose client -- Acceptor -acceptAll :: Socket -> (Socket -> HTTPRequest -> IO ()) -> IO () +acceptAll :: Socket -> Handler -> IO () acceptAll sock cback = do (client, _) <- NS.accept sock handleRequest client cback acceptAll sock cback -- Creates Acceptor pools -createAcceptorPool :: Socket -> Int -> (Socket -> HTTPRequest -> IO ()) -> IO () +createAcceptorPool :: Socket -> Int -> Handler -> IO () createAcceptorPool sock max cback = case max of 0 -> acceptAll sock cback @@ -76,8 +94,11 @@ getHostaddr "*" = return NS.iNADDR_ANY getHostaddr host = NS.inet_addr host --- Listens for incoming HTTP request -listenHTTP :: String -> PortNumber -> (Socket -> HTTPRequest -> IO ()) -> IO () +-- |Listens for incoming HTTP request +listenHTTP :: String -- ^ Address to bind (ie. "*") + -> PortNumber -- ^ Port to listen on + -> Handler -- ^ Route function to call + -> IO () listenHTTP hostname port cback = withSocketsDo $ do -- Get hostname host <- getHostaddr hostname
@@ -9,14 +9,16 @@ import Ketchup.Httpd import Ketchup.Utils import Network -import qualified Text.Regex.Posix as R +import qualified Text.Regex.PCRE as R -route :: [(C.ByteString, (Socket -> HTTPRequest - -> (M.Map C.ByteString C.ByteString) -> IO ()))] +-- |Router function +-- Takes a list of routes and iterates through them for every requeust +route :: [(C.ByteString, (Socket -> HTTPRequest + -> (M.Map C.ByteString C.ByteString) -> IO ()))] -- ^ Routes -> (Socket -> HTTPRequest -> IO ()) route [] handle request = sendNotFound handle route (r:routes) handle request - | match (uri request) (fst r) = (snd r) handle request $ + | match (uri request) (fst r) = (snd r) handle request $ params (uri request) (fst r) | otherwise = route routes handle request @@ -35,7 +37,7 @@ params :: C.ByteString -> C.ByteString -> M.Map C.ByteString C.ByteString params url template = - M.fromList $ filter (not . C.null . fst) $ + M.fromList $ filter (not . C.null . fst) $ zipWith retrieve urlparts tmpparts where retrieve x y
@@ -13,9 +13,10 @@ import Network.Mime import System.Directory (doesFileExist) --- Static file handler-static :: B.ByteString -> Socket -> HTTPRequest - -> (M.Map B.ByteString B.ByteString) -> IO ()+-- |Static file handler+-- Takes a directory and returns a route+static :: B.ByteString -- ^ Path to serve static files from+ -> Socket -> HTTPRequest -> (M.Map B.ByteString B.ByteString) -> IO () static folder hnd req params = do let path = B.concat [folder, uri req] let sane = sanecheck path@@ -25,12 +26,12 @@ True -> B.readFile strPath >>= sendReply hnd 200 [("Content-Type",[mime])] where- mime = defaultMimeLookup $ decodeUtf8 fname + mime = defaultMimeLookup $ decodeUtf8 fname fname = last $ B.split '/' path False -> sendNotFound hnd sanecheck :: B.ByteString -> Bool-sanecheck url = +sanecheck url = and checks where checks = [parentcheck]
@@ -2,21 +2,25 @@ module Ketchup.Utils ( trim +, breakBS +, parseBody , sendReply , sendBadRequest , sendNotFound , statusMsg ) where -import qualified Data.ByteString as B -import qualified Data.ByteString.Char8 as C +import qualified Data.ByteString.Char8 as B import Data.Char (isSpace) import qualified Data.List as List +import qualified Data.Map as Map import Network import Network.Socket.ByteString --- Returns status message from status id -statusMsg :: Int -> B.ByteString +-- |Status Messages +-- Returns status message from a given status id +statusMsg :: Int -- ^ Status code (ex. 404) + -> B.ByteString -- ^ Status message (ex. "404 Not Found") statusMsg stat -- 200 Success | stat == 200 = "200 OK" @@ -36,27 +40,51 @@ | otherwise = "500 Internal Server Error" -- Premade HTTP replies + +-- |Send 400 Bad Request error sendBadRequest :: Socket -> IO () -sendBadRequest client = do +sendBadRequest client = sendReply client 400 [("Content-Type",["text/plain"])] "400 Bad Request!\n" +-- |Send 404 Not Found error sendNotFound :: Socket -> IO () -sendNotFound client = do +sendNotFound client = sendReply client 404 [("Content-Type",["text/plain"])] "404 Not Found!\n" --- Template for 200 Reply -sendReply :: Socket -> Int -> [(B.ByteString, [B.ByteString])] -> B.ByteString -> IO () -sendReply client status headers body = do +-- |Send a HTTP reply +-- Sends a reply with the given parameters +sendReply :: Socket -- ^ Socket to write to + -> Int -- ^ Status Code to send + -> [(B.ByteString, [B.ByteString])] -- ^ HTTP headers ("Header",["value1", "value2"]) + -> B.ByteString -- ^ Body + -> IO () +sendReply client status headers body = sendAll client reply where - reply = B.concat ["HTTP/1.1 ", statusMsg status,"\r\n\ - \Content-Length: ", C.pack $ show $ C.length body, "\r\n\ - \Connection: close\r\n",heads,"\r\n",body] + reply = B.concat + [ "HTTP/1.1 ", statusMsg status,"\r\n" + , "Content-Length: ", B.pack $ show $ B.length body, "\r\n" + , "Connection: close\r\n", heads, "\r\n", body] -- Turn ("a", ["b", "c"]) headers into "a: b,c" - heads = B.concat $ map (\x -> B.concat [fst x, ": ", B.concat $ List.intersperse "," $ snd x, "\r\n"]) headers - + heads = B.concat $ map toHeader headers + toHeader x = B.concat [fst x, ": " + ,B.concat $ List.intersperse "," $ snd x + ,"\r\n"] --- Util function for trimming whitespace from headers +-- |Trim whitespace from headers trim :: B.ByteString -> B.ByteString trim = f . f - where f = C.reverse . C.dropWhile isSpace + where f = B.reverse . B.dropWhile isSpace + +-- |ByteString breakSubstring wrapper that drops delimiters +breakBS :: B.ByteString -> B.ByteString -> (B.ByteString, B.ByteString) +breakBS delimiter source = + (first, second) + where + first = fst broke + second = B.drop (B.length delimiter) $ snd broke + broke = B.breakSubstring delimiter source + +-- |Parse a URL-encoded Request +parseBody :: B.ByteString -> Map.Map B.ByteString B.ByteString +parseBody body = Map.fromList $ map (breakBS "=") $ B.split '&' body
@@ -1,6 +1,7 @@ # Howdy, this is Ketchup! -Ketchup is a very small HTTP server implementation, the HTTPd itself is one file with less than 100 LoC! +Ketchup is a very small HTTP server implementation, the HTTPd itself is one file with +around 100 LoC! Ketchup is also made to be as modular and embeddable as possible, if you want a cabal-free Haskell app, just take those two/three files you need and put it in your project!
@@ -10,13 +10,13 @@ import Ketchup.Chunked import Ketchup.Static -handle hnd req params = do +handle hnd req params = sendReply hnd 200 [("Content-Type", ["text/html"])] response where response = B.concat ["<center>You requested <b>", url, "</b></center>"] url = uri req -greet hnd req params = do +greet hnd req params = sendReply hnd 200 [("Content-Type", ["text/html"])] response where response = B.concat ["<h1>Hi ", getName name, "!</h1>"] @@ -30,7 +30,15 @@ chunk hnd "AAAAAAAAAHHH" endchunk hnd -router = route [("/", handle), ("/greet/:user", greet), - ("/chunk", chunked), ("/Ketchup/(.*)", static ".")] +post hnd req params = do + print $ parseBody $ body req + sendReply hnd 200 [("Content-Type", ["text/html"])] "OK!" + +router = route [ ("/" , handle ) + , ("/greet/:user" , greet ) + , ("/chunk" , chunked ) + , ("/Ketchup/(.*)", static "." ) + , ("/post" , post ) + ] main = do listenHTTP "*" 8080 router