├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── epoll_multiprocess_server.c ├── htdocs ├── README ├── check.cgi ├── color.cgi ├── coolblog.html ├── css │ └── style.css ├── favicon.png ├── hello.html ├── images │ └── tx.jpg ├── index.html └── js │ ├── application.js │ ├── busuanzi.pure.mini.js │ ├── hm.js │ ├── jquery.min.js │ └── plugin.min.js ├── httpd.c ├── httpd.h ├── java ├── static │ ├── 400.html │ ├── 403.html │ ├── 404.html │ ├── 500.html │ ├── coolblog.xyz.html │ ├── coolblog.xyz_files │ │ ├── application.js │ │ ├── busuanzi.pure.mini.js │ │ ├── hm.js │ │ ├── jquery.min(1).js │ │ ├── jquery.min.js │ │ ├── plugin.min.js │ │ ├── style.css │ │ └── tx.jpg │ ├── index.html │ └── meta │ │ └── content-type.json └── xyz │ └── coolblog │ └── httpd │ ├── ContentTypeUtils.java │ ├── Headers.java │ ├── HttpConstant.java │ ├── Response.java │ ├── ResponseHeaders.java │ ├── StatusCodeEnum.java │ ├── TinyHttpd.java │ ├── exception │ └── InvalidHeaderException.java │ └── json │ ├── BeautifyJsonUtils.java │ ├── JSONParser.java │ ├── exception │ ├── JsonParseException.java │ └── JsonTypeException.java │ ├── model │ ├── JsonArray.java │ └── JsonObject.java │ ├── parser │ └── Parser.java │ └── tokenizer │ ├── CharReader.java │ ├── Token.java │ ├── TokenList.java │ ├── TokenType.java │ └── Tokenizer.java ├── multiprocess_server.c ├── poll_server.c ├── select_server.c └── single_process_server.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | .vscode/ 55 | 56 | *.class 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: single_process_server multiprocess_server select_server poll_server epoll_multiprocess_server 2 | .PHONY: all 3 | 4 | single_process_server: single_process_server.o httpd.o 5 | cc -o single_process_server single_process_server.o httpd.o 6 | 7 | multiprocess_server: multiprocess_server.o httpd.o 8 | cc -o multiprocess_server multiprocess_server.o httpd.o 9 | 10 | select_server: select_server.o httpd.o 11 | cc -o select_server select_server.o httpd.o 12 | 13 | poll_server: poll_server.o httpd.o 14 | cc -o poll_server poll_server.o httpd.o 15 | 16 | epoll_multiprocess_server: epoll_multiprocess_server.o httpd.o 17 | cc -o epoll_multiprocess_server epoll_multiprocess_server.o httpd.o 18 | 19 | httpd.o: httpd.c 20 | cc -g -c httpd.c 21 | 22 | single_process_server.o: single_process_server.c httpd.h 23 | cc -g -c single_process_server.c 24 | 25 | multiprocess_server.o: multiprocess_server.c httpd.h 26 | cc -c multiprocess_server.c 27 | 28 | select_server.o: select_server.c httpd.h 29 | cc -c select_server.c 30 | 31 | poll_server.o: poll_server.c httpd.h 32 | cc -c poll_server.c 33 | 34 | epoll_multiprocess_server.o: epoll_multiprocess_server.c httpd.h 35 | cc -c epoll_multiprocess_server.c 36 | 37 | clean: 38 | rm httpd.o single_process_server single_process_server.o \ 39 | multiprocess_server multiprocess_server.o select_server.o select_server \ 40 | poll_server poll_server.o epoll_multiprocess_server epoll_multiprocess_server.o 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # toyhttpd 2 | ## 1.简介 3 | I/O 模型练手代码,分别使用阻塞式 I/O、select、poll 和 epoll 和 Java NIO 实现了简单的 HTTP Server。 4 | 5 | 本仓库对应于 Java NIO 系列文章,文章地址为[http://www.coolblog.xyz/categories/foundation-of-java/NIO/](http://www.coolblog.xyz/categories/foundation-of-java/NIO/) 6 | 7 | ## 2.使用说明 8 | ### 2.1 C代码编译 9 | C 代码可直接使用 make 命令编译,但因 epoll 相关接口是 Linux 系统特有的,所以应保证在 Linux 平台下编译。 10 | 11 | ### 2.2 12 | Java 代码并未依赖第三方 Jar 包,所以可直接使用 javac 命令编译。 13 | 14 | ```shell 15 | cd java 16 | javac xyz/coolblog/httpd/TinyHttpd.java 17 | ``` -------------------------------------------------------------------------------- /epoll_multiprocess_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "httpd.h" 15 | 16 | #define DEFAULT_PORT 8080 17 | #define MAX_EVENT_NUM 1024 18 | #define INFTIM -1 19 | 20 | void process(int); 21 | 22 | void handle_subprocess_exit(int); 23 | 24 | int main(int argc, char *argv[]) 25 | { 26 | struct sockaddr_in server_addr; 27 | int listen_fd; 28 | int cpu_core_num; 29 | int on = 1; 30 | 31 | listen_fd = socket(AF_INET, SOCK_STREAM, 0); 32 | fcntl(listen_fd, F_SETFL, O_NONBLOCK); 33 | setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 34 | 35 | bzero(&server_addr, sizeof(server_addr)); 36 | server_addr.sin_family = AF_INET; 37 | server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 38 | server_addr.sin_port = htons(DEFAULT_PORT); 39 | 40 | if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { 41 | perror("bind error, message: "); 42 | exit(1); 43 | } 44 | 45 | if (listen(listen_fd, 5) == -1) { 46 | perror("listen error, message: "); 47 | exit(1); 48 | } 49 | 50 | printf("listening 8080\n"); 51 | 52 | signal(SIGCHLD, handle_subprocess_exit); 53 | 54 | cpu_core_num = get_nprocs(); 55 | printf("cpu core num: %d\n", cpu_core_num); 56 | for (int i = 0; i < cpu_core_num * 2; i++) { 57 | pid_t pid = fork(); 58 | if (pid == 0) { 59 | process(listen_fd); 60 | exit(0); 61 | } 62 | } 63 | 64 | while (1) { 65 | sleep(1); 66 | } 67 | 68 | return 0; 69 | } 70 | 71 | void process(int listen_fd) 72 | { 73 | int conn_fd; 74 | int ready_fd_num; 75 | struct sockaddr_in client_addr; 76 | int client_addr_size = sizeof(client_addr); 77 | char buf[128]; 78 | 79 | struct epoll_event ev, events[MAX_EVENT_NUM]; 80 | int epoll_fd = epoll_create(MAX_EVENT_NUM); 81 | ev.data.fd = listen_fd; 82 | ev.events = EPOLLIN; 83 | 84 | if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev) == -1) { 85 | perror("epoll_ctl error, message: "); 86 | exit(1); 87 | } 88 | 89 | while(1) { 90 | ready_fd_num = epoll_wait(epoll_fd, events, MAX_EVENT_NUM, INFTIM); 91 | printf("[pid %d] 😱 震惊!我又被唤醒了...\n", getpid()); 92 | if (ready_fd_num == -1) { 93 | perror("epoll_wait error, message: "); 94 | continue; 95 | } 96 | for(int i = 0; i < ready_fd_num; i++) { 97 | if (events[i].data.fd == listen_fd) { 98 | conn_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &client_addr_size); 99 | if (conn_fd == -1) { 100 | sprintf(buf, "[pid %d] ❌ accept 出错了: ", getpid()); 101 | perror(buf); 102 | continue; 103 | } 104 | 105 | if (fcntl(conn_fd, F_SETFL, fcntl(conn_fd, F_GETFD, 0) | O_NONBLOCK) == -1) { 106 | continue; 107 | } 108 | 109 | ev.data.fd = conn_fd; 110 | ev.events = EPOLLIN; 111 | if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_fd, &ev) == -1) { 112 | perror("epoll_ctl error, message: "); 113 | close(conn_fd); 114 | } 115 | printf("[pid %d] 📡 收到来自 %s:%d 的请求\n", getpid(), inet_ntoa(client_addr.sin_addr), client_addr.sin_port); 116 | } else if (events[i].events & EPOLLIN) { 117 | printf("[pid %d] ✅ 处理来自 %s:%d 的请求\n", getpid(), inet_ntoa(client_addr.sin_addr), client_addr.sin_port); 118 | conn_fd = events[i].data.fd; 119 | accept_request(conn_fd, &client_addr); 120 | close(conn_fd); 121 | } else if (events[i].events & EPOLLERR) { 122 | fprintf(stderr, "epoll error\n"); 123 | close(conn_fd); 124 | } 125 | } 126 | } 127 | } 128 | 129 | void handle_subprocess_exit(int signo) 130 | { 131 | printf("clean subprocess.\n"); 132 | int status; 133 | while(waitpid(-1, &status, WNOHANG) > 0); 134 | } -------------------------------------------------------------------------------- /htdocs/README: -------------------------------------------------------------------------------- 1 | These are sample CGI scripts and webpages for tinyhttpd. They can 2 | be redistributed under the terms of the GPL. 3 | 4 | The most impressive demonstration I gave of tinyhttpd to my 5 | professor and my classmates was to load color.cgi with a value of 6 | "chartreuse." :) It's actually a very simple script, guys. 7 | 8 | jdb 9 | -------------------------------------------------------------------------------- /htdocs/check.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/perl -Tw 2 | 3 | use strict; 4 | use CGI; 5 | 6 | my($cgi) = new CGI; 7 | 8 | print $cgi->header('text/html'); 9 | print $cgi->start_html(-title => "Example CGI script", 10 | -BGCOLOR => 'red'); 11 | print $cgi->h1("CGI Example"); 12 | print $cgi->p, "This is an example of CGI\n"; 13 | print $cgi->p, "Parameters given to this script:\n"; 14 | print "
    \n"; 15 | foreach my $param ($cgi->param) 16 | { 17 | print "
  • ", "$param ", $cgi->param($param), "\n"; 18 | } 19 | print "
"; 20 | print $cgi->end_html, "\n"; 21 | -------------------------------------------------------------------------------- /htdocs/color.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/perl -Tw 2 | 3 | use strict; 4 | use CGI; 5 | 6 | my($cgi) = new CGI; 7 | 8 | print $cgi->header; 9 | my($color) = "blue"; 10 | $color = $cgi->param('color') if defined $cgi->param('color'); 11 | 12 | print $cgi->start_html(-title => uc($color), 13 | -BGCOLOR => $color); 14 | print $cgi->h1("This is $color"); 15 | print $cgi->end_html; 16 | -------------------------------------------------------------------------------- /htdocs/coolblog.html: -------------------------------------------------------------------------------- 1 | 2 | 酷博 - coolblog
-------------------------------------------------------------------------------- /htdocs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4wt/toyhttpd/2811926274b3e248f7e9cdd9f1658d81e07d71d4/htdocs/favicon.png -------------------------------------------------------------------------------- /htdocs/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 欢迎访问 6 | 7 | 8 | 真是一个奇妙的过程。 9 | 10 | -------------------------------------------------------------------------------- /htdocs/images/tx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4wt/toyhttpd/2811926274b3e248f7e9cdd9f1658d81e07d71d4/htdocs/images/tx.jpg -------------------------------------------------------------------------------- /htdocs/index.html: -------------------------------------------------------------------------------- 1 | 2 | HelloWorld 3 | 4 |

Welcome to J. David's webserver.

5 | 6 | 7 | -------------------------------------------------------------------------------- /htdocs/js/application.js: -------------------------------------------------------------------------------- 1 | // build time:Wed Jan 31 2018 08:29:04 GMT+0800 (CST) 2 | $(function(){$('[data-toggle="tooltip"]').tooltip();if(typeof $.fn.slimScroll!="undefined"){$(".sidebar .slimContent").slimScroll({height:"auto",color:"rgba(0,0,0,0.2)",size:"3px"})}$(".geopattern").each(function(){$(this).geopattern($(this).data("pattern-id"))});var t=$("#nav-main").okayNav({swipe_enabled:false});$(".donate-box").on("click",".pay_item",function(){var t=$(this).attr("data-id");var a=$(this).attr("data-src")?$(this).attr("data-src"):"assets/images/donate/"+t+"img.png";var e=t=="alipay"?"支付宝":"微信";$(this).addClass("checked").siblings(".pay_item").removeClass("checked");$(".donate-payimg img").attr("src",a);$("#donate-pay_txt").text(e)});$("[data-stick-bottom]").keepInView({fixed:false,parentClass:"has-sticky",customClass:"sticky",trigger:"bottom",zindex:42,edgeOffset:0});$("[data-stick-top]").keepInView({fixed:true,parentClass:"has-sticky",customClass:"sticky",trigger:"top",zindex:42,edgeOffset:0})}); 3 | //rebuild by neat -------------------------------------------------------------------------------- /htdocs/js/busuanzi.pure.mini.js: -------------------------------------------------------------------------------- 1 | var bszCaller,bszTag;!function(){var c,d,e,a=!1,b=[];ready=function(c){return a||"interactive"===document.readyState||"complete"===document.readyState?c.call(document):b.push(function(){return c.call(this)}),this},d=function(){for(var a=0,c=b.length;c>a;a++)b[a].apply(document);b=[]},e=function(){a||(a=!0,d.call(window),document.removeEventListener?document.removeEventListener("DOMContentLoaded",e,!1):document.attachEvent&&(document.detachEvent("onreadystatechange",e),window==window.top&&(clearInterval(c),c=null)))},document.addEventListener?document.addEventListener("DOMContentLoaded",e,!1):document.attachEvent&&(document.attachEvent("onreadystatechange",function(){/loaded|complete/.test(document.readyState)&&e()}),window==window.top&&(c=setInterval(function(){try{a||document.documentElement.doScroll("left")}catch(b){return}e()},5)))}(),bszCaller={fetch:function(a,b){var c="BusuanziCallback_"+Math.floor(1099511627776*Math.random());window[c]=this.evalCall(b),a=a.replace("=BusuanziCallback","="+c),scriptTag=document.createElement("SCRIPT"),scriptTag.type="text/javascript",scriptTag.defer=!0,scriptTag.src=a,document.getElementsByTagName("HEAD")[0].appendChild(scriptTag)},evalCall:function(a){return function(b){ready(function(){try{a(b),scriptTag.parentElement.removeChild(scriptTag)}catch(c){bszTag.hides()}})}}},bszCaller.fetch("//busuanzi.ibruce.info/busuanzi?jsonpCallback=BusuanziCallback",function(a){bszTag.texts(a),bszTag.shows()}),bszTag={bszs:["site_pv","page_pv","site_uv"],texts:function(a){this.bszs.map(function(b){var c=document.getElementById("busuanzi_value_"+b);c&&(c.innerHTML=a[b])})},hides:function(){this.bszs.map(function(a){var b=document.getElementById("busuanzi_container_"+a);b&&(b.style.display="none")})},shows:function(){this.bszs.map(function(a){var b=document.getElementById("busuanzi_container_"+a);b&&(b.style.display="inline")})}}; -------------------------------------------------------------------------------- /htdocs/js/hm.js: -------------------------------------------------------------------------------- 1 | (function(){var h={},mt={},c={id:"f9f999c0a6ec73a93d881968646841dd",dm:["coolblog.xyz"],js:"tongji.baidu.com/hm-web/js/",etrk:[],icon:'',ctrk:false,align:-1,nv:-1,vdur:1800000,age:31536000000,rec:0,rp:[],trust:0,vcard:0,qiao:0,lxb:0,conv:0,med:0,cvcc:'',cvcf:[],apps:''};var q=void 0,r=!0,t=null,u=!1;mt.cookie={};mt.cookie.set=function(a,b,d){var f;d.H&&(f=new Date,f.setTime(f.getTime()+d.H));document.cookie=a+"="+b+(d.domain?"; domain="+d.domain:"")+(d.path?"; path="+d.path:"")+(f?"; expires="+f.toGMTString():"")+(d.hb?"; secure":"")};mt.cookie.get=function(a){return(a=RegExp("(^| )"+a+"=([^;]*)(;|$)").exec(document.cookie))?a[2]:t};mt.h={};mt.h.oa=function(a){return document.getElementById(a)}; 2 | mt.h.J=function(a,b){var d=[],f=[];if(!a)return f;for(;a.parentNode!=t;){for(var g=0,n=0,l=a.parentNode.childNodes.length,p=0;p"+d.join(">"):l,f.push(l)),d.unshift(encodeURIComponent(String(a.nodeName).toLowerCase())+(1"));return f}; 3 | mt.h.$a=function(a){return(a=mt.h.J(a,r))&&a.length?String(a[0]):""};mt.h.Za=function(a){return mt.h.J(a,u)};mt.h.Xa=function(a,b){for(b=b.toUpperCase();(a=a.parentNode)&&1==a.nodeType;)if(a.tagName==b)return a;return t};mt.h.pa=function(a){return 9===a.nodeType?a:a.ownerDocument||a.document}; 4 | mt.h.Ya=function(a){var b={top:0,left:0};if(!a)return b;var d=mt.h.pa(a).documentElement;"undefined"!==typeof a.getBoundingClientRect&&(b=a.getBoundingClientRect());return{top:b.top+(window.pageYOffset||d.scrollTop)-(d.clientTop||0),left:b.left+(window.pageXOffset||d.scrollLeft)-(d.clientLeft||0)}}; 5 | (mt.h.Ea=function(){function a(){if(!a.A){a.A=r;for(var b=0,d=f.length;ba?"0"+a:a}var d={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};return function(d){switch(typeof d){case "undefined":return"undefined";case "number":return isFinite(d)?String(d):"null";case "string":return a(d);case "boolean":return String(d); 12 | default:if(d===t)return"null";if(d instanceof Array){var g=["["],n=d.length,l,p,e;for(p=0;p(new Date).getTime())return a.substring(b+1)}}else if(mt.localStorage.C())try{return mt.localStorage.g.load(document.location.hostname),mt.localStorage.g.getAttribute(a)}catch(f){}return t}; 17 | mt.localStorage.remove=function(a){if(window.localStorage)window.localStorage.removeItem(a);else if(mt.localStorage.C())try{mt.localStorage.g.load(document.location.hostname),mt.localStorage.g.removeAttribute(a),mt.localStorage.g.save(document.location.hostname)}catch(b){}};mt.sessionStorage={};mt.sessionStorage.set=function(a,b){if(window.sessionStorage)try{window.sessionStorage.setItem(a,b)}catch(d){}}; 18 | mt.sessionStorage.get=function(a){return window.sessionStorage?window.sessionStorage.getItem(a):t};mt.sessionStorage.remove=function(a){window.sessionStorage&&window.sessionStorage.removeItem(a)};mt.aa={};mt.aa.log=function(a,b){var d=new Image,f="mini_tangram_log_"+Math.floor(2147483648*Math.random()).toString(36);window[f]=d;d.onload=d.onerror=d.onabort=function(){d.onload=d.onerror=d.onabort=t;d=window[f]=t;b&&b(a)};d.src=a};mt.S={}; 19 | mt.S.ua=function(){var a="";if(navigator.plugins&&navigator.mimeTypes.length){var b=navigator.plugins["Shockwave Flash"];b&&b.description&&(a=b.description.replace(/^.*\s+(\S+)\s+\S+$/,"$1"))}else if(window.ActiveXObject)try{if(b=new ActiveXObject("ShockwaveFlash.ShockwaveFlash"))(a=b.GetVariable("$version"))&&(a=a.replace(/^.*\s+(\d+),(\d+).*$/,"$1.$2"))}catch(d){}return a}; 20 | mt.S.Wa=function(a,b,d,f,g){return''};mt.url={}; 21 | mt.url.i=function(a,b){var d=a.match(RegExp("(^|&|\\?|#)("+b+")=([^&#]*)(&|$|#)",""));return d?d[3]:t};mt.url.ab=function(a){return(a=a.match(/^(https?:)\/\//))?a[1]:t};mt.url.ra=function(a){return(a=a.match(/^(https?:\/\/)?([^\/\?#]*)/))?a[2].replace(/.*@/,""):t};mt.url.W=function(a){return(a=mt.url.ra(a))?a.replace(/:\d+$/,""):a};mt.url.J=function(a){return(a=a.match(/^(https?:\/\/)?[^\/]*(.*)/))?a[2].replace(/[\?#].*/,"").replace(/^$/,"/"):t}; 22 | (function(){function a(){for(var a=u,d=document.getElementsByTagName("script"),f=d.length,f=100a.length)){var b=a[1],m=a[4]||3;if(0b&&0m){e.B++;for(var s=(h.b.a.cv||"*").split("!"),f=s.length;fb.length)){var e=d.j(b[1]);b=d.j(b[2]);if(e!==q&&b!==q){var m=decodeURIComponent(h.b.getData("Hm_ct_"+c.id)||""),m=a(m,e,1,b);h.b.setData("Hm_ct_"+c.id,encodeURIComponent(m),c.age)}}},_setVisitTag:function(b){if(!(3>b.length)){var f=d.j(b[1]);b=d.j(b[2]);if(f!==q&&b!==q){var m=e.o.T,m=a(m,f,2,b);e.o.T=m}}},_setPageTag:function(b){if(!(3>b.length)){var f=d.j(b[1]);b=d.j(b[2]);if(f!==q&&b!==q){var m=e.o.page,m=a(m,f,3,b);e.o.page=m}}},_setReferrerOverride:function(a){1< 36 | a.length&&(h.b.a.su=a[1].charAt&&"/"===a[1].charAt(0)?n.protocol+"//"+window.location.host+a[1]:a[1],e.Y=r)},_trackOrder:function(a){a=a[1];d.d(a,"Object")&&(b(a),e.e|=16,h.b.a.nv=0,h.b.a.st=4,h.b.a.et=94,h.b.a.ep=f.stringify(a),h.b.k())},_trackMobConv:function(a){if(a={webim:1,tel:2,map:3,sms:4,callback:5,share:6}[a[1]])e.e|=32,h.b.a.et=93,h.b.a.ep=a,h.b.k()},_trackRTPageview:function(a){a=a[1];d.d(a,"Object")&&(b(a),a=f.stringify(a),512>=encodeURIComponent(a).length&&(e.e|=64,h.b.a.rt=a))},_trackRTEvent:function(a){a= 37 | a[1];if(d.d(a,"Object")){b(a);a=encodeURIComponent(f.stringify(a));var g=function(a){var b=h.b.a.rt;e.e|=128;h.b.a.et=90;h.b.a.rt=a;h.b.k();h.b.a.rt=b},m=a.length;if(900>=m)g.call(this,a);else for(var m=Math.ceil(m/900),s="block|"+Math.round(Math.random()*n.P).toString(16)+"|"+m+"|",l=[],v=0;vc.vdur?1:4;var a= 40 | u;this.z(document.referrer)&&this.z(document.location.href)?a=r:(a=b.W(document.referrer),a=this.N(a||"",document.location.hostname));return a?k.m-k.r>c.vdur?1:4:3},getData:function(a){try{return n.get(a)||e.get(a)||p.get(a)}catch(b){}},setData:function(a,b,d){try{n.set(a,b,{domain:this.I(),path:this.V(),H:d}),d?p.set(a,b,d):e.set(a,b)}catch(f){}},Fa:function(a){try{n.set(a,"",{domain:this.I(),path:this.V(),H:-1}),e.remove(a),p.remove(a)}catch(b){}},Ka:function(){var a,b,d,e,f;k.r=this.getData("Hm_lpvt_"+ 41 | c.id)||0;13===k.r.length&&(k.r=Math.round(k.r/1E3));b=this.ta();a=4!==b?1:0;if(d=this.getData("Hm_lvt_"+c.id)){e=d.split(",");for(f=e.length-1;0<=f;f--)13===e[f].length&&(e[f]=""+Math.round(e[f]/1E3));for(;2592E3e.length?2:3;for(1===a&&e.push(k.m);4=+new Date-e;);};if(c.med)n="/zoosnet",g="swt",l=/swt|zixun|call|chat|zoos|business|talk|kefu|openkf|online|\/LR\/Chatpre\.aspx/i,p={click:function(){for(var a=[],b=e(document.getElementsByTagName("a")),b=[].concat.apply(b,e(document.getElementsByTagName("area"))),b=[].concat.apply(b,e(document.getElementsByTagName("img"))),d,f,g=0,k=b.length;g line. 10 | * 2) Comment out the line that defines the variable newthread. 11 | * 3) Comment out the two lines that run pthread_create(). 12 | * 4) Uncomment the line that runs accept_request(). 13 | * 5) Remove -lsocket from the Makefile. 14 | */ 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "httpd.h" 32 | 33 | #define ISspace(x) isspace((int)(x)) 34 | 35 | #define SERVER_STRING "Server: coolblog-httpd/1.0\r\n" 36 | #define STDIN 0 37 | #define STDOUT 1 38 | #define STDERR 2 39 | 40 | /**********************************************************************/ 41 | /* A request has caused a call to accept() on the server port to 42 | * return. Process the request appropriately. 43 | * Parameters: the socket connected to the client */ 44 | /**********************************************************************/ 45 | void accept_request(int client, struct sockaddr_in *client_addr) 46 | { 47 | char buf[1024]; 48 | char time_buf[50]; 49 | char *log_buf[5]; 50 | size_t numchars; 51 | char method[255]; 52 | char url[255]; 53 | char path[512]; 54 | size_t i, j; 55 | struct stat st; 56 | int cgi = 0; /* becomes true if server decides this is a CGI 57 | * program */ 58 | char *query_string = NULL; 59 | 60 | log_buf[0] = inet_ntoa(client_addr->sin_addr); 61 | 62 | // 获取请求头的第一行 63 | numchars = get_line(client, buf, sizeof(buf)); 64 | 65 | log_buf[1] = buf; 66 | 67 | printf("%s - - [%s] %s", log_buf[0], get_time_str(time_buf), log_buf[1]); 68 | 69 | // 获取请求方法 70 | i = 0; j = 0; 71 | while (!ISspace(buf[i]) && (i < sizeof(method) - 1)) 72 | { 73 | method[i] = buf[i]; 74 | i++; 75 | } 76 | j=i; 77 | method[i] = '\0'; 78 | 79 | // 判断请求方法是否所允许的方法 GET 和 POST 80 | if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) 81 | { 82 | unimplemented(client); 83 | return; 84 | } 85 | 86 | if (strcasecmp(method, "POST") == 0) 87 | cgi = 1; 88 | 89 | i = 0; 90 | // 消耗 http method 后面多余的空格 91 | while (ISspace(buf[j]) && (j < numchars)) 92 | j++; 93 | // 获取请求路径 94 | while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < numchars)) 95 | { 96 | url[i] = buf[j]; 97 | i++; j++; 98 | } 99 | url[i] = '\0'; 100 | 101 | // 处理 http method 为 GET 的请求 102 | if (strcasecmp(method, "GET") == 0) 103 | { 104 | query_string = url; 105 | // 定位 url 中 ? 的位置 106 | while ((*query_string != '?') && (*query_string != '\0')) 107 | query_string++; 108 | // 将 ? 替换为 \0 109 | if (*query_string == '?') 110 | { 111 | cgi = 1; 112 | *query_string = '\0'; 113 | query_string++; 114 | } 115 | } 116 | 117 | // 根据 url 拼接资源路径,比如请求 /test.html,拼接后的路径为 htdocs/test.html 118 | sprintf(path, "htdocs%s", url); 119 | if (path[strlen(path) - 1] == '/') 120 | strcat(path, "index.html"); 121 | // 读取资源 122 | if (stat(path, &st) == -1) { 123 | // 读取并丢弃其他的请求头 124 | while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ 125 | numchars = get_line(client, buf, sizeof(buf)); 126 | // 返回404响应 127 | not_found(client); 128 | } 129 | else 130 | { 131 | if ((st.st_mode & S_IFMT) == S_IFDIR) 132 | strcat(path, "/index.html"); 133 | if ((st.st_mode & S_IXUSR) || 134 | (st.st_mode & S_IXGRP) || 135 | (st.st_mode & S_IXOTH) ) 136 | cgi = 1; 137 | if (!cgi) 138 | // 返回200响应 139 | serve_file(client, path); 140 | else 141 | execute_cgi(client, path, method, query_string); 142 | } 143 | } 144 | 145 | /**********************************************************************/ 146 | /* Inform the client that a request it has made has a problem. 147 | * Parameters: client socket */ 148 | /**********************************************************************/ 149 | void bad_request(int client) 150 | { 151 | char buf[1024]; 152 | 153 | sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n"); 154 | send(client, buf, sizeof(buf), 0); 155 | sprintf(buf, "Content-type: text/html\r\n"); 156 | send(client, buf, sizeof(buf), 0); 157 | sprintf(buf, "\r\n"); 158 | send(client, buf, sizeof(buf), 0); 159 | sprintf(buf, "

Your browser sent a bad request, "); 160 | send(client, buf, sizeof(buf), 0); 161 | sprintf(buf, "such as a POST without a Content-Length.\r\n"); 162 | send(client, buf, sizeof(buf), 0); 163 | } 164 | 165 | /**********************************************************************/ 166 | /* Put the entire contents of a file out on a socket. This function 167 | * is named after the UNIX "cat" command, because it might have been 168 | * easier just to do something like pipe, fork, and exec("cat"). 169 | * Parameters: the client socket descriptor 170 | * FILE pointer for the file to cat */ 171 | /**********************************************************************/ 172 | void cat(int client, FILE *resource) 173 | { 174 | char buf[1024]; 175 | 176 | fgets(buf, sizeof(buf), resource); 177 | while (!feof(resource)) 178 | { 179 | send(client, buf, strlen(buf), 0); 180 | fgets(buf, sizeof(buf), resource); 181 | } 182 | } 183 | 184 | /**********************************************************************/ 185 | /* Inform the client that a CGI script could not be executed. 186 | * Parameter: the client socket descriptor. */ 187 | /**********************************************************************/ 188 | void cannot_execute(int client) 189 | { 190 | char buf[1024]; 191 | 192 | sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n"); 193 | send(client, buf, strlen(buf), 0); 194 | sprintf(buf, "Content-type: text/html\r\n"); 195 | send(client, buf, strlen(buf), 0); 196 | sprintf(buf, "\r\n"); 197 | send(client, buf, strlen(buf), 0); 198 | sprintf(buf, "

Error prohibited CGI execution.\r\n"); 199 | send(client, buf, strlen(buf), 0); 200 | } 201 | 202 | /**********************************************************************/ 203 | /* Print out an error message with perror() (for system errors; based 204 | * on value of errno, which indicates system call errors) and exit the 205 | * program indicating an error. */ 206 | /**********************************************************************/ 207 | void error_die(const char *sc) 208 | { 209 | perror(sc); 210 | exit(1); 211 | } 212 | 213 | /**********************************************************************/ 214 | /* Execute a CGI script. Will need to set environment variables as 215 | * appropriate. 216 | * Parameters: client socket descriptor 217 | * path to the CGI script */ 218 | /**********************************************************************/ 219 | void execute_cgi(int client, const char *path, 220 | const char *method, const char *query_string) 221 | { 222 | char buf[1024]; 223 | int cgi_output[2]; 224 | int cgi_input[2]; 225 | pid_t pid; 226 | int status; 227 | int i; 228 | char c; 229 | int numchars = 1; 230 | int content_length = -1; 231 | 232 | buf[0] = 'A'; buf[1] = '\0'; 233 | if (strcasecmp(method, "GET") == 0) 234 | while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ 235 | numchars = get_line(client, buf, sizeof(buf)); 236 | else if (strcasecmp(method, "POST") == 0) /*POST*/ 237 | { 238 | numchars = get_line(client, buf, sizeof(buf)); 239 | while ((numchars > 0) && strcmp("\n", buf)) 240 | { 241 | buf[15] = '\0'; 242 | if (strcasecmp(buf, "Content-Length:") == 0) 243 | content_length = atoi(&(buf[16])); 244 | numchars = get_line(client, buf, sizeof(buf)); 245 | } 246 | if (content_length == -1) { 247 | bad_request(client); 248 | return; 249 | } 250 | } 251 | else/*HEAD or other*/ 252 | { 253 | } 254 | 255 | 256 | if (pipe(cgi_output) < 0) { 257 | cannot_execute(client); 258 | return; 259 | } 260 | if (pipe(cgi_input) < 0) { 261 | cannot_execute(client); 262 | return; 263 | } 264 | 265 | if ( (pid = fork()) < 0 ) { 266 | cannot_execute(client); 267 | return; 268 | } 269 | sprintf(buf, "HTTP/1.0 200 OK\r\n"); 270 | send(client, buf, strlen(buf), 0); 271 | if (pid == 0) /* child: CGI script */ 272 | { 273 | char meth_env[255]; 274 | char query_env[255]; 275 | char length_env[255]; 276 | 277 | dup2(cgi_output[1], STDOUT); 278 | dup2(cgi_input[0], STDIN); 279 | close(cgi_output[0]); 280 | close(cgi_input[1]); 281 | sprintf(meth_env, "REQUEST_METHOD=%s", method); 282 | putenv(meth_env); 283 | if (strcasecmp(method, "GET") == 0) { 284 | sprintf(query_env, "QUERY_STRING=%s", query_string); 285 | putenv(query_env); 286 | } 287 | else { /* POST */ 288 | sprintf(length_env, "CONTENT_LENGTH=%d", content_length); 289 | putenv(length_env); 290 | } 291 | execl(path, NULL); 292 | exit(0); 293 | } else { /* parent */ 294 | close(cgi_output[1]); 295 | close(cgi_input[0]); 296 | if (strcasecmp(method, "POST") == 0) 297 | for (i = 0; i < content_length; i++) { 298 | recv(client, &c, 1, 0); 299 | write(cgi_input[1], &c, 1); 300 | } 301 | while (read(cgi_output[0], &c, 1) > 0) 302 | send(client, &c, 1, 0); 303 | 304 | close(cgi_output[0]); 305 | close(cgi_input[1]); 306 | waitpid(pid, &status, 0); 307 | } 308 | } 309 | 310 | /**********************************************************************/ 311 | /* Get a line from a socket, whether the line ends in a newline, 312 | * carriage return, or a CRLF combination. Terminates the string read 313 | * with a null character. If no newline indicator is found before the 314 | * end of the buffer, the string is terminated with a null. If any of 315 | * the above three line terminators is read, the last character of the 316 | * string will be a linefeed and the string will be terminated with a 317 | * null character. 318 | * Parameters: the socket descriptor 319 | * the buffer to save the data in 320 | * the size of the buffer 321 | * Returns: the number of bytes stored (excluding null) */ 322 | /**********************************************************************/ 323 | int get_line(int sock, char *buf, int size) 324 | { 325 | int i = 0; 326 | char c = '\0'; 327 | int n; 328 | 329 | while ((i < size - 1) && (c != '\n')) 330 | { 331 | n = recv(sock, &c, 1, 0); 332 | /* DEBUG printf("%02X\n", c); */ 333 | if (n > 0) 334 | { 335 | if (c == '\r') 336 | { 337 | n = recv(sock, &c, 1, MSG_PEEK); 338 | /* DEBUG printf("%02X\n", c); */ 339 | if ((n > 0) && (c == '\n')) 340 | recv(sock, &c, 1, 0); 341 | else 342 | c = '\n'; 343 | } 344 | buf[i] = c; 345 | i++; 346 | } 347 | else 348 | c = '\n'; 349 | } 350 | buf[i] = '\0'; 351 | 352 | return(i); 353 | } 354 | 355 | /**********************************************************************/ 356 | /* Return the informational HTTP headers about a file. */ 357 | /* Parameters: the socket to print the headers on 358 | * the name of the file */ 359 | /**********************************************************************/ 360 | void headers(int client, const char *filename) 361 | { 362 | char buf[1024]; 363 | char suffix[8]; 364 | char type[24]; 365 | get_file_suffix(filename, suffix); 366 | suffix2type(suffix, type); 367 | 368 | strcpy(buf, "HTTP/1.0 200 OK\r\n"); 369 | send(client, buf, strlen(buf), 0); 370 | strcpy(buf, SERVER_STRING); 371 | send(client, buf, strlen(buf), 0); 372 | sprintf(buf, "Content-Type: %s\r\n", type); 373 | send(client, buf, strlen(buf), 0); 374 | strcpy(buf, "\r\n"); 375 | send(client, buf, strlen(buf), 0); 376 | } 377 | 378 | void get_file_suffix(const char *filename, char *suffix) 379 | { 380 | int suffix_len = 0; 381 | for (int i = strlen(filename) - 1; i > 0; i--) 382 | { 383 | if (filename[i] != '.') 384 | { 385 | suffix_len++; 386 | continue; 387 | } 388 | 389 | strcpy(suffix, filename + i + 1); 390 | suffix[suffix_len] = '\0'; 391 | break; 392 | } 393 | } 394 | 395 | void suffix2type(const char *suffix, char *type) 396 | { 397 | if (strcasecmp(suffix, "html") == 0) 398 | { 399 | strcpy(type, "text/html"); 400 | } 401 | else if (strcasecmp(suffix, "htm") == 0) 402 | { 403 | strcpy(type, "text/html"); 404 | } 405 | else if (strcasecmp(suffix, "txt") == 0) 406 | { 407 | strcpy(type, "text/plain"); 408 | } 409 | else if (strcasecmp(suffix, "xml") == 0) 410 | { 411 | strcpy(type, "text/xml"); 412 | } 413 | else if (strcasecmp(suffix, "js") == 0) 414 | { 415 | strcpy(type, "application/javascript"); 416 | } 417 | else if (strcasecmp(suffix, "css") == 0) 418 | { 419 | strcpy(type, "text/css"); 420 | } 421 | else if (strcasecmp(suffix, "pdf") == 0) 422 | { 423 | strcpy(type, "application/pdf"); 424 | } 425 | else if (strcasecmp(suffix, "json") == 0) 426 | { 427 | strcpy(type, "application/json"); 428 | } 429 | else if (strcasecmp(suffix, "jpg") == 0) 430 | { 431 | strcpy(type, "image/jpeg"); 432 | } 433 | else if (strcasecmp(suffix, "png") == 0) 434 | { 435 | strcpy(type, "image/png"); 436 | } 437 | else if (strcasecmp(suffix, "ico") == 0) 438 | { 439 | strcpy(type, "image/x-icon"); 440 | } 441 | else if (strcasecmp(suffix, "gif") == 0) 442 | { 443 | strcpy(type, "image/gif"); 444 | } 445 | else if (strcasecmp(suffix, "tif") == 0) 446 | { 447 | strcpy(type, "image/tiff"); 448 | } 449 | else if (strcasecmp(suffix, "bmp") == 0) 450 | { 451 | strcpy(type, "application/x-bmp"); 452 | } 453 | else 454 | { 455 | strcpy(type, "application/octet-stream"); 456 | } 457 | } 458 | 459 | /**********************************************************************/ 460 | /* Give a client a 404 not found status message. */ 461 | /**********************************************************************/ 462 | void not_found(int client) 463 | { 464 | char buf[1024]; 465 | 466 | sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n"); 467 | send(client, buf, strlen(buf), 0); 468 | sprintf(buf, SERVER_STRING); 469 | send(client, buf, strlen(buf), 0); 470 | sprintf(buf, "Content-Type: text/html\r\n"); 471 | send(client, buf, strlen(buf), 0); 472 | sprintf(buf, "\r\n"); 473 | send(client, buf, strlen(buf), 0); 474 | sprintf(buf, "Not Found\r\n"); 475 | send(client, buf, strlen(buf), 0); 476 | sprintf(buf, "

The server could not fulfill\r\n"); 477 | send(client, buf, strlen(buf), 0); 478 | sprintf(buf, "your request because the resource specified\r\n"); 479 | send(client, buf, strlen(buf), 0); 480 | sprintf(buf, "is unavailable or nonexistent.\r\n"); 481 | send(client, buf, strlen(buf), 0); 482 | sprintf(buf, "\r\n"); 483 | send(client, buf, strlen(buf), 0); 484 | } 485 | 486 | /**********************************************************************/ 487 | /* Send a regular file to the client. Use headers, and report 488 | * errors to client if they occur. 489 | * Parameters: a pointer to a file structure produced from the socket 490 | * file descriptor 491 | * the name of the file to serve */ 492 | /**********************************************************************/ 493 | void serve_file(int client, const char *filename) 494 | { 495 | FILE *resource = NULL; 496 | int numchars = 1; 497 | char buf[1024]; 498 | int n; 499 | 500 | buf[0] = 'A'; buf[1] = '\0'; 501 | // 读取并丢弃其他消息头 502 | while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ 503 | numchars = get_line(client, buf, sizeof(buf)); 504 | 505 | resource = fopen(filename, is_text_type(filename) ? "r" : "rb"); 506 | if (resource == NULL) 507 | not_found(client); 508 | else 509 | { 510 | headers(client, filename); 511 | cat(client, resource); 512 | } 513 | fclose(resource); 514 | } 515 | 516 | int is_text_type(const char *filename) 517 | { 518 | char suffix[8]; 519 | get_file_suffix(filename, suffix); 520 | 521 | if (strcasecmp(suffix, "html") == 0) 522 | { 523 | return 1; 524 | } 525 | else if (strcasecmp(suffix, "htm") == 0) 526 | { 527 | return 1; 528 | } 529 | else if (strcasecmp(suffix, "txt") == 0) 530 | { 531 | return 1; 532 | } 533 | else if (strcasecmp(suffix, "xml") == 0) 534 | { 535 | return 1; 536 | } 537 | else if (strcasecmp(suffix, "js") == 0) 538 | { 539 | return 1; 540 | } 541 | else if (strcasecmp(suffix, "css") == 0) 542 | { 543 | return 1; 544 | } 545 | else if (strcasecmp(suffix, "json") == 0) 546 | { 547 | return 1; 548 | } 549 | else 550 | { 551 | return 0; 552 | } 553 | } 554 | 555 | /**********************************************************************/ 556 | /* This function starts the process of listening for web connections 557 | * on a specified port. If the port is 0, then dynamically allocate a 558 | * port and modify the original port variable to reflect the actual 559 | * port. 560 | * Parameters: pointer to variable containing the port to connect on 561 | * Returns: the socket */ 562 | /**********************************************************************/ 563 | int startup(u_short *port) 564 | { 565 | int httpd = 0; 566 | int on = 1; 567 | struct sockaddr_in name; 568 | 569 | httpd = socket(PF_INET, SOCK_STREAM, 0); 570 | if (httpd == -1) 571 | error_die("socket"); 572 | memset(&name, 0, sizeof(name)); 573 | name.sin_family = AF_INET; 574 | name.sin_port = htons(*port); 575 | name.sin_addr.s_addr = htonl(INADDR_ANY); 576 | if ((setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0) 577 | { 578 | error_die("setsockopt failed"); 579 | } 580 | if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0) 581 | error_die("bind"); 582 | if (*port == 0) /* if dynamically allocating a port */ 583 | { 584 | socklen_t namelen = sizeof(name); 585 | if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1) 586 | error_die("getsockname"); 587 | *port = ntohs(name.sin_port); 588 | } 589 | if (listen(httpd, 5) < 0) 590 | error_die("listen"); 591 | return(httpd); 592 | } 593 | 594 | /**********************************************************************/ 595 | /* Inform the client that the requested web method has not been 596 | * implemented. 597 | * Parameter: the client socket */ 598 | /**********************************************************************/ 599 | void unimplemented(int client) 600 | { 601 | char buf[1024]; 602 | 603 | sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n"); 604 | send(client, buf, strlen(buf), 0); 605 | sprintf(buf, SERVER_STRING); 606 | send(client, buf, strlen(buf), 0); 607 | sprintf(buf, "Content-Type: text/html\r\n"); 608 | send(client, buf, strlen(buf), 0); 609 | sprintf(buf, "\r\n"); 610 | send(client, buf, strlen(buf), 0); 611 | sprintf(buf, "Method Not Implemented\r\n"); 612 | send(client, buf, strlen(buf), 0); 613 | sprintf(buf, "\r\n"); 614 | send(client, buf, strlen(buf), 0); 615 | sprintf(buf, "

HTTP request method not supported.\r\n"); 616 | send(client, buf, strlen(buf), 0); 617 | sprintf(buf, "\r\n"); 618 | send(client, buf, strlen(buf), 0); 619 | } 620 | 621 | char* get_time_str(char buf[]) 622 | { 623 | time_t now; 624 | struct tm *tm; 625 | time(&now); 626 | tm = localtime(&now); 627 | sprintf(buf, "%s", asctime(tm)); 628 | buf[strlen(buf) - 1] = '\0'; 629 | return buf; 630 | } -------------------------------------------------------------------------------- /httpd.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTPD_H 2 | #define HTTPD_H 3 | 4 | void accept_request(int, struct sockaddr_in *); 5 | void bad_request(int); 6 | void cat(int, FILE *); 7 | void cannot_execute(int); 8 | void error_die(const char *); 9 | void execute_cgi(int, const char *, const char *, const char *); 10 | int get_line(int, char *, int); 11 | void headers(int, const char *); 12 | void suffix2type(const char *, char *); 13 | int is_text_type(const char *); 14 | void get_file_suffix(const char *, char *); 15 | void not_found(int); 16 | void serve_file(int, const char *); 17 | int startup(u_short *); 18 | void unimplemented(int); 19 | char* get_time_str(char[]); 20 | 21 | #endif -------------------------------------------------------------------------------- /java/static/400.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 400 Bad Request 5 | 6 | 7 |

400 Bad Request!

8 | 9 |

© 2018 coolblog.xyz

10 | 11 | -------------------------------------------------------------------------------- /java/static/403.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 Fobidden 5 | 6 | 7 |

403 Fobidden!

8 | 9 |

© 2018 coolblog.xyz

10 | 11 | -------------------------------------------------------------------------------- /java/static/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 404 Not Found 5 | 6 | 7 |

404 Not Found!

8 | 9 |

© 2018 coolblog.xyz

10 | 11 | -------------------------------------------------------------------------------- /java/static/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 500 Internal Server Error 5 | 6 | 7 |

500 Internal Server Error!

8 | 9 |

© 2018 coolblog.xyz

10 | 11 | -------------------------------------------------------------------------------- /java/static/coolblog.xyz.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 酷博 - coolblog
-------------------------------------------------------------------------------- /java/static/coolblog.xyz_files/application.js: -------------------------------------------------------------------------------- 1 | // build time:Sun Mar 25 2018 01:27:43 GMT+0800 (CST) 2 | $(function(){$('[data-toggle="tooltip"]').tooltip();if(typeof $.fn.slimScroll!="undefined"){$(".sidebar .slimContent").slimScroll({height:"auto",color:"rgba(0,0,0,0.2)",size:"3px"})}$(".geopattern").each(function(){$(this).geopattern($(this).data("pattern-id"))});var t=$("#nav-main").okayNav({swipe_enabled:false});$(".donate-box").on("click",".pay_item",function(){var t=$(this).attr("data-id");var a=$(this).attr("data-src")?$(this).attr("data-src"):"assets/images/donate/"+t+"img.png";var e=t=="alipay"?"支付宝":"微信";$(this).addClass("checked").siblings(".pay_item").removeClass("checked");$(".donate-payimg img").attr("src",a);$("#donate-pay_txt").text(e)});$("[data-stick-bottom]").keepInView({fixed:false,parentClass:"has-sticky",customClass:"sticky",trigger:"bottom",zindex:42,edgeOffset:0});$("[data-stick-top]").keepInView({fixed:true,parentClass:"has-sticky",customClass:"sticky",trigger:"top",zindex:42,edgeOffset:0})}); 3 | //rebuild by neat -------------------------------------------------------------------------------- /java/static/coolblog.xyz_files/busuanzi.pure.mini.js: -------------------------------------------------------------------------------- 1 | var bszCaller,bszTag;!function(){var c,d,e,a=!1,b=[];ready=function(c){return a||"interactive"===document.readyState||"complete"===document.readyState?c.call(document):b.push(function(){return c.call(this)}),this},d=function(){for(var a=0,c=b.length;c>a;a++)b[a].apply(document);b=[]},e=function(){a||(a=!0,d.call(window),document.removeEventListener?document.removeEventListener("DOMContentLoaded",e,!1):document.attachEvent&&(document.detachEvent("onreadystatechange",e),window==window.top&&(clearInterval(c),c=null)))},document.addEventListener?document.addEventListener("DOMContentLoaded",e,!1):document.attachEvent&&(document.attachEvent("onreadystatechange",function(){/loaded|complete/.test(document.readyState)&&e()}),window==window.top&&(c=setInterval(function(){try{a||document.documentElement.doScroll("left")}catch(b){return}e()},5)))}(),bszCaller={fetch:function(a,b){var c="BusuanziCallback_"+Math.floor(1099511627776*Math.random());window[c]=this.evalCall(b),a=a.replace("=BusuanziCallback","="+c),scriptTag=document.createElement("SCRIPT"),scriptTag.type="text/javascript",scriptTag.defer=!0,scriptTag.src=a,document.getElementsByTagName("HEAD")[0].appendChild(scriptTag)},evalCall:function(a){return function(b){ready(function(){try{a(b),scriptTag.parentElement.removeChild(scriptTag)}catch(c){bszTag.hides()}})}}},bszCaller.fetch("//busuanzi.ibruce.info/busuanzi?jsonpCallback=BusuanziCallback",function(a){bszTag.texts(a),bszTag.shows()}),bszTag={bszs:["site_pv","page_pv","site_uv"],texts:function(a){this.bszs.map(function(b){var c=document.getElementById("busuanzi_value_"+b);c&&(c.innerHTML=a[b])})},hides:function(){this.bszs.map(function(a){var b=document.getElementById("busuanzi_container_"+a);b&&(b.style.display="none")})},shows:function(){this.bszs.map(function(a){var b=document.getElementById("busuanzi_container_"+a);b&&(b.style.display="inline")})}}; -------------------------------------------------------------------------------- /java/static/coolblog.xyz_files/hm.js: -------------------------------------------------------------------------------- 1 | (function(){var h={},mt={},c={id:"f9f999c0a6ec73a93d881968646841dd",dm:["coolblog.xyz"],js:"tongji.baidu.com/hm-web/js/",etrk:[],icon:'',ctrk:false,align:-1,nv:-1,vdur:1800000,age:31536000000,rec:0,rp:[],trust:0,vcard:0,qiao:0,lxb:0,conv:0,med:0,cvcc:'',cvcf:[],apps:''};var q=void 0,r=!0,t=null,u=!1;mt.cookie={};mt.cookie.set=function(a,b,d){var f;d.H&&(f=new Date,f.setTime(f.getTime()+d.H));document.cookie=a+"="+b+(d.domain?"; domain="+d.domain:"")+(d.path?"; path="+d.path:"")+(f?"; expires="+f.toGMTString():"")+(d.hb?"; secure":"")};mt.cookie.get=function(a){return(a=RegExp("(^| )"+a+"=([^;]*)(;|$)").exec(document.cookie))?a[2]:t};mt.h={};mt.h.oa=function(a){return document.getElementById(a)}; 2 | mt.h.J=function(a,b){var d=[],f=[];if(!a)return f;for(;a.parentNode!=t;){for(var g=0,n=0,l=a.parentNode.childNodes.length,p=0;p"+d.join(">"):l,f.push(l)),d.unshift(encodeURIComponent(String(a.nodeName).toLowerCase())+(1"));return f}; 3 | mt.h.$a=function(a){return(a=mt.h.J(a,r))&&a.length?String(a[0]):""};mt.h.Za=function(a){return mt.h.J(a,u)};mt.h.Xa=function(a,b){for(b=b.toUpperCase();(a=a.parentNode)&&1==a.nodeType;)if(a.tagName==b)return a;return t};mt.h.pa=function(a){return 9===a.nodeType?a:a.ownerDocument||a.document}; 4 | mt.h.Ya=function(a){var b={top:0,left:0};if(!a)return b;var d=mt.h.pa(a).documentElement;"undefined"!==typeof a.getBoundingClientRect&&(b=a.getBoundingClientRect());return{top:b.top+(window.pageYOffset||d.scrollTop)-(d.clientTop||0),left:b.left+(window.pageXOffset||d.scrollLeft)-(d.clientLeft||0)}}; 5 | (mt.h.Ea=function(){function a(){if(!a.A){a.A=r;for(var b=0,d=f.length;ba?"0"+a:a}var d={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};return function(d){switch(typeof d){case "undefined":return"undefined";case "number":return isFinite(d)?String(d):"null";case "string":return a(d);case "boolean":return String(d); 12 | default:if(d===t)return"null";if(d instanceof Array){var g=["["],n=d.length,l,p,e;for(p=0;p(new Date).getTime())return a.substring(b+1)}}else if(mt.localStorage.C())try{return mt.localStorage.g.load(document.location.hostname),mt.localStorage.g.getAttribute(a)}catch(f){}return t}; 17 | mt.localStorage.remove=function(a){if(window.localStorage)window.localStorage.removeItem(a);else if(mt.localStorage.C())try{mt.localStorage.g.load(document.location.hostname),mt.localStorage.g.removeAttribute(a),mt.localStorage.g.save(document.location.hostname)}catch(b){}};mt.sessionStorage={};mt.sessionStorage.set=function(a,b){if(window.sessionStorage)try{window.sessionStorage.setItem(a,b)}catch(d){}}; 18 | mt.sessionStorage.get=function(a){return window.sessionStorage?window.sessionStorage.getItem(a):t};mt.sessionStorage.remove=function(a){window.sessionStorage&&window.sessionStorage.removeItem(a)};mt.aa={};mt.aa.log=function(a,b){var d=new Image,f="mini_tangram_log_"+Math.floor(2147483648*Math.random()).toString(36);window[f]=d;d.onload=d.onerror=d.onabort=function(){d.onload=d.onerror=d.onabort=t;d=window[f]=t;b&&b(a)};d.src=a};mt.S={}; 19 | mt.S.ua=function(){var a="";if(navigator.plugins&&navigator.mimeTypes.length){var b=navigator.plugins["Shockwave Flash"];b&&b.description&&(a=b.description.replace(/^.*\s+(\S+)\s+\S+$/,"$1"))}else if(window.ActiveXObject)try{if(b=new ActiveXObject("ShockwaveFlash.ShockwaveFlash"))(a=b.GetVariable("$version"))&&(a=a.replace(/^.*\s+(\d+),(\d+).*$/,"$1.$2"))}catch(d){}return a}; 20 | mt.S.Wa=function(a,b,d,f,g){return''};mt.url={}; 21 | mt.url.i=function(a,b){var d=a.match(RegExp("(^|&|\\?|#)("+b+")=([^&#]*)(&|$|#)",""));return d?d[3]:t};mt.url.ab=function(a){return(a=a.match(/^(https?:)\/\//))?a[1]:t};mt.url.ra=function(a){return(a=a.match(/^(https?:\/\/)?([^\/\?#]*)/))?a[2].replace(/.*@/,""):t};mt.url.W=function(a){return(a=mt.url.ra(a))?a.replace(/:\d+$/,""):a};mt.url.J=function(a){return(a=a.match(/^(https?:\/\/)?[^\/]*(.*)/))?a[2].replace(/[\?#].*/,"").replace(/^$/,"/"):t}; 22 | (function(){function a(){for(var a=u,d=document.getElementsByTagName("script"),f=d.length,f=100a.length)){var b=a[1],m=a[4]||3;if(0b&&0m){e.B++;for(var s=(h.b.a.cv||"*").split("!"),f=s.length;fb.length)){var e=d.j(b[1]);b=d.j(b[2]);if(e!==q&&b!==q){var m=decodeURIComponent(h.b.getData("Hm_ct_"+c.id)||""),m=a(m,e,1,b);h.b.setData("Hm_ct_"+c.id,encodeURIComponent(m),c.age)}}},_setVisitTag:function(b){if(!(3>b.length)){var f=d.j(b[1]);b=d.j(b[2]);if(f!==q&&b!==q){var m=e.o.T,m=a(m,f,2,b);e.o.T=m}}},_setPageTag:function(b){if(!(3>b.length)){var f=d.j(b[1]);b=d.j(b[2]);if(f!==q&&b!==q){var m=e.o.page,m=a(m,f,3,b);e.o.page=m}}},_setReferrerOverride:function(a){1< 36 | a.length&&(h.b.a.su=a[1].charAt&&"/"===a[1].charAt(0)?n.protocol+"//"+window.location.host+a[1]:a[1],e.Y=r)},_trackOrder:function(a){a=a[1];d.d(a,"Object")&&(b(a),e.e|=16,h.b.a.nv=0,h.b.a.st=4,h.b.a.et=94,h.b.a.ep=f.stringify(a),h.b.k())},_trackMobConv:function(a){if(a={webim:1,tel:2,map:3,sms:4,callback:5,share:6}[a[1]])e.e|=32,h.b.a.et=93,h.b.a.ep=a,h.b.k()},_trackRTPageview:function(a){a=a[1];d.d(a,"Object")&&(b(a),a=f.stringify(a),512>=encodeURIComponent(a).length&&(e.e|=64,h.b.a.rt=a))},_trackRTEvent:function(a){a= 37 | a[1];if(d.d(a,"Object")){b(a);a=encodeURIComponent(f.stringify(a));var g=function(a){var b=h.b.a.rt;e.e|=128;h.b.a.et=90;h.b.a.rt=a;h.b.k();h.b.a.rt=b},m=a.length;if(900>=m)g.call(this,a);else for(var m=Math.ceil(m/900),s="block|"+Math.round(Math.random()*n.P).toString(16)+"|"+m+"|",l=[],v=0;vc.vdur?1:4;var a= 40 | u;this.z(document.referrer)&&this.z(document.location.href)?a=r:(a=b.W(document.referrer),a=this.N(a||"",document.location.hostname));return a?k.m-k.r>c.vdur?1:4:3},getData:function(a){try{return n.get(a)||e.get(a)||p.get(a)}catch(b){}},setData:function(a,b,d){try{n.set(a,b,{domain:this.I(),path:this.V(),H:d}),d?p.set(a,b,d):e.set(a,b)}catch(f){}},Fa:function(a){try{n.set(a,"",{domain:this.I(),path:this.V(),H:-1}),e.remove(a),p.remove(a)}catch(b){}},Ka:function(){var a,b,d,e,f;k.r=this.getData("Hm_lpvt_"+ 41 | c.id)||0;13===k.r.length&&(k.r=Math.round(k.r/1E3));b=this.ta();a=4!==b?1:0;if(d=this.getData("Hm_lvt_"+c.id)){e=d.split(",");for(f=e.length-1;0<=f;f--)13===e[f].length&&(e[f]=""+Math.round(e[f]/1E3));for(;2592E3e.length?2:3;for(1===a&&e.push(k.m);4=+new Date-e;);};if(c.med)n="/zoosnet",g="swt",l=/swt|zixun|call|chat|zoos|business|talk|kefu|openkf|online|\/LR\/Chatpre\.aspx/i,p={click:function(){for(var a=[],b=e(document.getElementsByTagName("a")),b=[].concat.apply(b,e(document.getElementsByTagName("area"))),b=[].concat.apply(b,e(document.getElementsByTagName("img"))),d,f,g=0,k=b.length;g 2 | 3 | 4 | index 5 | 6 | 7 |

hello world!

8 |

hello world!

9 |

hello world!

10 | 11 |

© 2018 coolblog.xyz

12 | 13 | -------------------------------------------------------------------------------- /java/static/meta/content-type.json: -------------------------------------------------------------------------------- 1 | { 2 | "evy": "application/envoy", 3 | "fif": "application/fractals", 4 | "spl": "application/futuresplash", 5 | "hta": "application/hta", 6 | "acx": "application/internet-property-stream", 7 | "hqx": "application/mac-binhex40", 8 | "doc": "application/msword", 9 | "dot": "application/msword", 10 | "*": "application/octet-stream", 11 | "bin": "application/octet-stream", 12 | "class": "application/octet-stream", 13 | "dms": "application/octet-stream", 14 | "exe": "application/octet-stream", 15 | "lha": "application/octet-stream", 16 | "lzh": "application/octet-stream", 17 | "oda": "application/oda", 18 | "axs": "application/olescript", 19 | "pdf": "application/pdf", 20 | "prf": "application/pics-rules", 21 | "p10": "application/pkcs10", 22 | "crl": "application/pkix-crl", 23 | "ai": "application/postscript", 24 | "eps": "application/postscript", 25 | "ps": "application/postscript", 26 | "rtf": "application/rtf", 27 | "setpay": "application/set-payment-initiation", 28 | "setreg": "application/set-registration-initiation", 29 | "xla": "application/vnd.ms-excel", 30 | "xlc": "application/vnd.ms-excel", 31 | "xlm": "application/vnd.ms-excel", 32 | "xls": "application/vnd.ms-excel", 33 | "xlt": "application/vnd.ms-excel", 34 | "xlw": "application/vnd.ms-excel", 35 | "msg": "application/vnd.ms-outlook", 36 | "sst": "application/vnd.ms-pkicertstore", 37 | "cat": "application/vnd.ms-pkiseccat", 38 | "stl": "application/vnd.ms-pkistl", 39 | "pot": "application/vnd.ms-powerpoint", 40 | "pps": "application/vnd.ms-powerpoint", 41 | "ppt": "application/vnd.ms-powerpoint", 42 | "mpp": "application/vnd.ms-project", 43 | "wcm": "application/vnd.ms-works", 44 | "wdb": "application/vnd.ms-works", 45 | "wks": "application/vnd.ms-works", 46 | "wps": "application/vnd.ms-works", 47 | "hlp": "application/winhlp", 48 | "bcpio": "application/x-bcpio", 49 | "cdf": "application/x-cdf", 50 | "z": "application/x-compress", 51 | "tgz": "application/x-compressed", 52 | "cpio": "application/x-cpio", 53 | "csh": "application/x-csh", 54 | "dcr": "application/x-director", 55 | "dir": "application/x-director", 56 | "dxr": "application/x-director", 57 | "dvi": "application/x-dvi", 58 | "gtar": "application/x-gtar", 59 | "gz": "application/x-gzip", 60 | "hdf": "application/x-hdf", 61 | "ins": "application/x-internet-signup", 62 | "isp": "application/x-internet-signup", 63 | "iii": "application/x-iphone", 64 | "js": "application/x-javascript", 65 | "latex": "application/x-latex", 66 | "mdb": "application/x-msaccess", 67 | "crd": "application/x-mscardfile", 68 | "clp": "application/x-msclip", 69 | "dll": "application/x-msdownload", 70 | "m13": "application/x-msmediaview", 71 | "m14": "application/x-msmediaview", 72 | "mvb": "application/x-msmediaview", 73 | "wmf": "application/x-msmetafile", 74 | "mny": "application/x-msmoney", 75 | "pub": "application/x-mspublisher", 76 | "scd": "application/x-msschedule", 77 | "trm": "application/x-msterminal", 78 | "wri": "application/x-mswrite", 79 | "cdf": "application/x-netcdf", 80 | "nc": "application/x-netcdf", 81 | "pma": "application/x-perfmon", 82 | "pmc": "application/x-perfmon", 83 | "pml": "application/x-perfmon", 84 | "pmr": "application/x-perfmon", 85 | "pmw": "application/x-perfmon", 86 | "p12": "application/x-pkcs12", 87 | "pfx": "application/x-pkcs12", 88 | "p7b": "application/x-pkcs7-certificates", 89 | "spc": "application/x-pkcs7-certificates", 90 | "p7r": "application/x-pkcs7-certreqresp", 91 | "p7c": "application/x-pkcs7-mime", 92 | "p7m": "application/x-pkcs7-mime", 93 | "p7s": "application/x-pkcs7-signature", 94 | "sh": "application/x-sh", 95 | "shar": "application/x-shar", 96 | "swf": "application/x-shockwave-flash", 97 | "sit": "application/x-stuffit", 98 | "sv4cpio": "application/x-sv4cpio", 99 | "sv4crc": "application/x-sv4crc", 100 | "tar": "application/x-tar", 101 | "tcl": "application/x-tcl", 102 | "tex": "application/x-tex", 103 | "texi": "application/x-texinfo", 104 | "texinfo": "application/x-texinfo", 105 | "roff": "application/x-troff", 106 | "t": "application/x-troff", 107 | "tr": "application/x-troff", 108 | "man": "application/x-troff-man", 109 | "me": "application/x-troff-me", 110 | "ms": "application/x-troff-ms", 111 | "ustar": "application/x-ustar", 112 | "src": "application/x-wais-source", 113 | "cer": "application/x-x509-ca-cert", 114 | "crt": "application/x-x509-ca-cert", 115 | "der": "application/x-x509-ca-cert", 116 | "pko": "application/ynd.ms-pkipko", 117 | "zip": "application/zip", 118 | 119 | "au": "audio/basic", 120 | "snd": "audio/basic", 121 | "mid": "audio/mid", 122 | "rmi": "audio/mid", 123 | "mp3": "audio/mpeg", 124 | "aif": "audio/x-aiff", 125 | "aifc": "audio/x-aiff", 126 | "aiff": "audio/x-aiff", 127 | "m3u": "audio/x-mpegurl", 128 | "ra": "audio/x-pn-realaudio", 129 | "ram": "audio/x-pn-realaudio", 130 | "wav": "audio/x-wav", 131 | 132 | "bmp": "image/bmp", 133 | "cod": "image/cis-cod", 134 | "gif": "image/gif", 135 | "ief": "image/ief", 136 | "jpe": "image/jpeg", 137 | "jpeg": "image/jpeg", 138 | "jpg": "image/jpeg", 139 | "jfif": "image/pipeg", 140 | "svg": "image/svg+xml", 141 | "tif": "image/tiff", 142 | "tiff": "image/tiff", 143 | "ras": "image/x-cmu-raster", 144 | "cmx": "image/x-cmx", 145 | "ico": "image/x-icon", 146 | "pnm": "image/x-portable-anymap", 147 | "pbm": "image/x-portable-bitmap", 148 | "pgm": "image/x-portable-graymap", 149 | "ppm": "image/x-portable-pixmap", 150 | "rgb": "image/x-rgb", 151 | "xbm": "image/x-xbitmap", 152 | "xpm": "image/x-xpixmap", 153 | "xwd": "image/x-xwindowdump", 154 | 155 | "css": "text/css", 156 | "323": "text/h323", 157 | "htm": "text/html", 158 | "html": "text/html", 159 | "stm": "text/html", 160 | "uls": "text/iuls", 161 | "bas": "text/plain", 162 | "c": "text/plain", 163 | "h": "text/plain", 164 | "txt": "text/plain", 165 | "rtx": "text/richtext", 166 | "sct": "text/scriptlet", 167 | "tsv": "text/tab-separated-values", 168 | "htt": "text/webviewhtml", 169 | "htc": "text/x-component", 170 | "etx": "text/x-setext", 171 | "vcf": "text/x-vcard", 172 | 173 | "mp2": "video/mpeg", 174 | "mpa": "video/mpeg", 175 | "mpe": "video/mpeg", 176 | "mpeg": "video/mpeg", 177 | "mpg": "video/mpeg", 178 | "mpv2": "video/mpeg", 179 | "mp4": "video/mp4", 180 | "mov": "video/quicktime", 181 | "qt": "video/quicktime", 182 | "lsf": "video/x-la-asf", 183 | "lsx": "video/x-la-asf", 184 | "asf": "video/x-ms-asf", 185 | "asr": "video/x-ms-asf", 186 | "asx": "video/x-ms-asf", 187 | "avi": "video/x-msvideo", 188 | "movie": "video/x-sgi-movie" 189 | } -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/ContentTypeUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd; 2 | 3 | import java.io.IOException; 4 | import java.io.RandomAccessFile; 5 | import java.nio.ByteBuffer; 6 | import java.nio.channels.FileChannel; 7 | import xyz.coolblog.httpd.json.JSONParser; 8 | import xyz.coolblog.httpd.json.model.JsonObject; 9 | 10 | /** 11 | * ContentTypeUtils 12 | * 13 | * @author coolblog.xyz 14 | * @date 2018-03-27 21:57:42 15 | */ 16 | public class ContentTypeUtils { 17 | 18 | private static JsonObject jsonObject; 19 | 20 | private static final String JSON_PATH = "static/meta/content-type.json"; 21 | 22 | static { 23 | JSONParser jsonParser = new JSONParser(); 24 | try { 25 | jsonObject = (JsonObject) jsonParser.fromJSON(readFile()); 26 | } catch (IOException e) { 27 | e.printStackTrace(); 28 | } 29 | } 30 | 31 | private static String readFile() { 32 | try { 33 | RandomAccessFile raf = new RandomAccessFile(JSON_PATH, "r"); 34 | FileChannel channel = raf.getChannel(); 35 | 36 | ByteBuffer buffer = ByteBuffer.allocate((int) channel.size()); 37 | buffer.clear(); 38 | channel.read(buffer); 39 | buffer.flip(); 40 | return new String(buffer.array()); 41 | } catch (Exception e) { 42 | e.printStackTrace(); 43 | } 44 | 45 | return null; 46 | } 47 | 48 | public static String getContentType(String ext) { 49 | return (String) jsonObject.get(ext); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/Headers.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * Headers 8 | * 9 | * @author coolblog.xyz 10 | * @date 2018-03-26 22:29:57 11 | */ 12 | public class Headers { 13 | 14 | private String method; 15 | 16 | private String path; 17 | 18 | private String version; 19 | 20 | private Map headerMap = new HashMap<>(); 21 | 22 | public Headers() { 23 | 24 | } 25 | 26 | public String getMethod() { 27 | return method; 28 | } 29 | 30 | public void setMethod(String method) { 31 | this.method = method; 32 | } 33 | 34 | public String getPath() { 35 | return path; 36 | } 37 | 38 | public void setPath(String path) { 39 | this.path = path; 40 | } 41 | 42 | public String getVersion() { 43 | return version; 44 | } 45 | 46 | public void setVersion(String version) { 47 | this.version = version; 48 | } 49 | 50 | public void set(String key, String value) { 51 | headerMap.put(key, value); 52 | } 53 | 54 | public String get(String key) { 55 | return headerMap.get(key); 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return "Headers{" + 61 | "method='" + method + '\'' + 62 | ", path='" + path + '\'' + 63 | ", version='" + version + '\'' + 64 | ", headerMap=" + headerMap + 65 | '}'; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/HttpConstant.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd; 2 | 3 | /** 4 | * HttpConstant 5 | * 6 | * @author coolblog.xyz 7 | * @date 2018-03-27 20:09:55 8 | */ 9 | public class HttpConstant { 10 | 11 | public static final String DEFAULT_HTTP_VERSION = "HTTP/1.1"; 12 | 13 | public static final String SERVER_NAME = "coolblog-xyz-httpd"; 14 | } 15 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/Response.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd; 2 | 3 | /** 4 | * Response 5 | * 6 | * @author coolblog.xyz 7 | * @date 2018-03-27 20:02:52 8 | */ 9 | public class Response { 10 | 11 | private int code; 12 | 13 | private String version; 14 | 15 | private String contentType; 16 | 17 | private String server; 18 | 19 | @Override 20 | public String toString() { 21 | 22 | return "Response{}"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/ResponseHeaders.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd; 2 | 3 | /** 4 | * ResponseHeaders 5 | * 6 | * @author coolblog.xyz 7 | * @date 2018-03-27 20:12:02 8 | */ 9 | public class ResponseHeaders extends Headers { 10 | 11 | private int code; 12 | 13 | private String phrase; 14 | 15 | private String contentType; 16 | 17 | private int contentLength; 18 | 19 | private String server; 20 | 21 | public ResponseHeaders(int code) { 22 | this.code = code; 23 | this.server = HttpConstant.SERVER_NAME; 24 | this.phrase = StatusCodeEnum.queryPhrase(code); 25 | setVersion(HttpConstant.DEFAULT_HTTP_VERSION); 26 | } 27 | 28 | public int getCode() { 29 | return code; 30 | } 31 | 32 | public void setCode(int code) { 33 | this.code = code; 34 | } 35 | 36 | public String getPhrase() { 37 | return phrase; 38 | } 39 | 40 | public void setPhrase(String phrase) { 41 | this.phrase = phrase; 42 | } 43 | 44 | public String getContentType() { 45 | return contentType; 46 | } 47 | 48 | public void setContentType(String contentType) { 49 | this.contentType = contentType; 50 | } 51 | 52 | public int getContentLength() { 53 | return contentLength; 54 | } 55 | 56 | public void setContentLength(int contentLength) { 57 | this.contentLength = contentLength; 58 | } 59 | 60 | public String getServer() { 61 | return server; 62 | } 63 | 64 | public void setServer(String server) { 65 | this.server = server; 66 | } 67 | 68 | @Override 69 | public String toString() { 70 | StringBuilder sb = new StringBuilder(); 71 | sb.append(String.format("%s %d %s\r\n", getVersion(), code, phrase)); 72 | sb.append(String.format("ContentType: %s\r\n", contentType)); 73 | sb.append(String.format("ContentLength: %d\r\n", contentLength)); 74 | sb.append(String.format("Server: %s\r\n", server)); 75 | sb.append("\r\n"); 76 | return sb.toString(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/StatusCodeEnum.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd; 2 | 3 | /** 4 | * StatusCodeEnum 5 | * 6 | * @author coolblog.xyz 7 | * @date 2018-03-27 12:29:43 8 | */ 9 | public enum StatusCodeEnum { 10 | OK(200, "OK"), 11 | BAD_REQUEST(400, "Bad Request"), 12 | FORBIDDEN(403, "Forbidden"), 13 | NOT_FOUND(404, "Not Found"), 14 | INTERNAL_SERVER_ERROR(500, "Internal Server Error"); 15 | 16 | private int code; 17 | 18 | private String phrase; 19 | 20 | StatusCodeEnum(int code, String phrase) { 21 | this.code = code; 22 | this.phrase = phrase; 23 | } 24 | 25 | public int getCode() { 26 | return code; 27 | } 28 | 29 | public void setCode(int code) { 30 | this.code = code; 31 | } 32 | 33 | public String getPhrase() { 34 | return phrase; 35 | } 36 | 37 | public void setPhrase(String phrase) { 38 | this.phrase = phrase; 39 | } 40 | 41 | public static String queryPhrase(int code) { 42 | for (StatusCodeEnum statusCodeEnum : StatusCodeEnum.values()) { 43 | if (statusCodeEnum.getCode() == code) { 44 | return statusCodeEnum.getPhrase(); 45 | } 46 | } 47 | 48 | return null; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/TinyHttpd.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.IOException; 5 | import java.io.RandomAccessFile; 6 | import java.net.InetSocketAddress; 7 | import java.nio.ByteBuffer; 8 | import java.nio.channels.FileChannel; 9 | import java.nio.channels.SelectionKey; 10 | import java.nio.channels.Selector; 11 | import java.nio.channels.ServerSocketChannel; 12 | import java.nio.channels.SocketChannel; 13 | import java.time.Instant; 14 | import java.util.Date; 15 | import java.util.Iterator; 16 | import java.util.Objects; 17 | import java.util.Optional; 18 | import java.util.Set; 19 | import xyz.coolblog.httpd.exception.InvalidHeaderException; 20 | 21 | import static xyz.coolblog.httpd.StatusCodeEnum.BAD_REQUEST; 22 | import static xyz.coolblog.httpd.StatusCodeEnum.FORBIDDEN; 23 | import static xyz.coolblog.httpd.StatusCodeEnum.INTERNAL_SERVER_ERROR; 24 | import static xyz.coolblog.httpd.StatusCodeEnum.NOT_FOUND; 25 | import static xyz.coolblog.httpd.StatusCodeEnum.OK; 26 | 27 | /** 28 | * TinyHttpd 29 | * 30 | * @author coolblog.xyz 31 | * @date 2018-03-26 22:28:44 32 | */ 33 | public class TinyHttpd { 34 | 35 | private static final int DEFAULT_PORT = 8080; 36 | 37 | private static final int DEFAULT_BUFFER_SIZE = 4096; 38 | 39 | private static final String INDEX_PAGE = "index.html"; 40 | 41 | private static final String STATIC_RESOURCE_DIR = "static"; 42 | 43 | private static final String META_RESOURCE_DIR_PREFIX = "/meta/"; 44 | 45 | private static final String KEY_VALUE_SEPARATOR = ":"; 46 | 47 | private static final String CRLF = "\r\n"; 48 | 49 | private int port; 50 | 51 | public TinyHttpd() { 52 | this(DEFAULT_PORT); 53 | } 54 | 55 | public TinyHttpd(int port) { 56 | this.port = port; 57 | } 58 | 59 | public void start() throws IOException { 60 | ServerSocketChannel ssc = ServerSocketChannel.open(); 61 | ssc.socket().bind(new InetSocketAddress("localhost", port)); 62 | ssc.configureBlocking(false); 63 | 64 | System.out.println(String.format("TinyHttpd 已启动,正在监听 %d 端口...", port)); 65 | 66 | Selector selector = Selector.open(); 67 | ssc.register(selector, SelectionKey.OP_ACCEPT); 68 | 69 | while(true) { 70 | int readyNum = selector.select(); 71 | if (readyNum == 0) { 72 | continue; 73 | } 74 | 75 | Set selectedKeys = selector.selectedKeys(); 76 | Iterator it = selectedKeys.iterator(); 77 | while (it.hasNext()) { 78 | SelectionKey selectionKey = it.next(); 79 | it.remove(); 80 | 81 | if (selectionKey.isAcceptable()) { 82 | SocketChannel socketChannel = ssc.accept(); 83 | socketChannel.configureBlocking(false); 84 | socketChannel.register(selector, SelectionKey.OP_READ); 85 | } else if (selectionKey.isReadable()) { 86 | request(selectionKey); 87 | selectionKey.interestOps(SelectionKey.OP_WRITE); 88 | } else if (selectionKey.isWritable()) { 89 | response(selectionKey); 90 | } 91 | } 92 | } 93 | } 94 | 95 | /** 96 | * 处理请求 97 | * @param selectionKey 选择键 98 | * @throws IOException 99 | */ 100 | private void request(SelectionKey selectionKey) throws IOException { 101 | SocketChannel channel = (SocketChannel) selectionKey.channel(); 102 | ByteBuffer buffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE); 103 | channel.read(buffer); 104 | 105 | buffer.flip(); 106 | byte[] bytes = new byte[buffer.limit()]; 107 | buffer.get(bytes); 108 | String headerStr = new String(bytes); 109 | try { 110 | Headers headers = parseHeader(headerStr); 111 | selectionKey.attach(Optional.of(headers)); 112 | } catch (InvalidHeaderException e) { 113 | selectionKey.attach(Optional.empty()); 114 | } 115 | } 116 | 117 | // 解析消息头 118 | private Headers parseHeader(String headerStr) { 119 | if (Objects.isNull(headerStr) || headerStr.isEmpty()) { 120 | throw new InvalidHeaderException(); 121 | } 122 | 123 | int index = headerStr.indexOf(CRLF); 124 | if (index == -1) { 125 | throw new InvalidHeaderException(); 126 | } 127 | 128 | Headers headers = new Headers(); 129 | String firstLine = headerStr.substring(0, index); 130 | String[] parts = firstLine.split(" "); 131 | 132 | /* 133 | * 请求头的第一行必须由三部分构成,分别为 METHOD PATH VERSION 134 | * 比如: 135 | * GET /index.html HTTP/1.1 136 | */ 137 | if (parts.length < 3) { 138 | throw new InvalidHeaderException(); 139 | } 140 | 141 | headers.setMethod(parts[0]); 142 | headers.setPath(parts[1]); 143 | headers.setVersion(parts[2]); 144 | 145 | parts = headerStr.split(CRLF); 146 | for (String part : parts) { 147 | index = part.indexOf(KEY_VALUE_SEPARATOR); 148 | if (index == -1) { 149 | continue; 150 | } 151 | String key = part.substring(0, index); 152 | if (index == -1 || index + 1 >= part.length()) { 153 | headers.set(key, ""); 154 | continue; 155 | } 156 | String value = part.substring(index + 1); 157 | headers.set(key, value); 158 | } 159 | 160 | return headers; 161 | } 162 | 163 | private void response(SelectionKey selectionKey) throws IOException { 164 | SocketChannel channel = (SocketChannel) selectionKey.channel(); 165 | Optional op = (Optional) selectionKey.attachment(); 166 | 167 | // 处理无效请求,返回 400 错误 168 | if (!op.isPresent()) { 169 | handleBadRequest(channel); 170 | channel.close(); 171 | return; 172 | } 173 | 174 | String ip = channel.getRemoteAddress().toString().replace("/", ""); 175 | Headers headers = op.get(); 176 | // 处理 403 177 | if (headers.getPath().startsWith(META_RESOURCE_DIR_PREFIX)) { 178 | handleForbidden(channel); 179 | channel.close(); 180 | log(ip, headers, FORBIDDEN.getCode()); 181 | return; 182 | } 183 | 184 | try { 185 | handleOK(channel, headers.getPath()); 186 | log(ip, headers, OK.getCode()); 187 | } catch (FileNotFoundException e) { 188 | handleNotFound(channel); 189 | log(ip, headers, NOT_FOUND.getCode()); 190 | } catch (Exception e) { 191 | handleInternalServerError(channel); 192 | log(ip, headers, INTERNAL_SERVER_ERROR.getCode()); 193 | } finally { 194 | channel.close(); 195 | } 196 | } 197 | 198 | private void handleOK(SocketChannel channel, String path) throws IOException { 199 | ResponseHeaders headers = new ResponseHeaders(OK.getCode()); 200 | 201 | ByteBuffer bodyBuffer = readFile(path); 202 | headers.setContentLength(bodyBuffer.capacity()); 203 | headers.setContentType(ContentTypeUtils.getContentType(getExtension(path))); 204 | ByteBuffer headerBuffer = ByteBuffer.wrap(headers.toString().getBytes()); 205 | 206 | channel.write(new ByteBuffer[]{headerBuffer, bodyBuffer}); 207 | } 208 | 209 | private String getExtension(String path) { 210 | if (path.endsWith("/")) { 211 | return "html"; 212 | } 213 | 214 | String finename = path.substring(path.lastIndexOf("/") + 1); 215 | int index = finename.lastIndexOf("."); 216 | return index == -1 ? "*" : finename.substring(index + 1); 217 | } 218 | 219 | private void handleNotFound(SocketChannel channel) { 220 | try { 221 | handleError(channel, NOT_FOUND.getCode()); 222 | } catch (Exception e) { 223 | handleInternalServerError(channel); 224 | } 225 | } 226 | 227 | private void handleBadRequest(SocketChannel channel) { 228 | try { 229 | handleError(channel, BAD_REQUEST.getCode()); 230 | } catch (Exception e) { 231 | handleInternalServerError(channel); 232 | } 233 | } 234 | 235 | private void handleForbidden(SocketChannel channel) { 236 | try { 237 | handleError(channel, FORBIDDEN.getCode()); 238 | } catch (Exception e) { 239 | handleInternalServerError(channel); 240 | } 241 | } 242 | 243 | private void handleInternalServerError(SocketChannel channel) { 244 | try { 245 | handleError(channel, INTERNAL_SERVER_ERROR.getCode()); 246 | } catch (Exception e) { 247 | e.printStackTrace(); 248 | } 249 | } 250 | 251 | private void handleError(SocketChannel channel, int statusCode) throws IOException { 252 | ResponseHeaders headers = new ResponseHeaders(statusCode); 253 | 254 | ByteBuffer bodyBuffer = readFile(String.format("/%d.html", statusCode)); 255 | headers.setContentLength(bodyBuffer.capacity()); 256 | headers.setContentType(ContentTypeUtils.getContentType("html")); 257 | ByteBuffer headerBuffer = ByteBuffer.wrap(headers.toString().getBytes()); 258 | 259 | channel.write(new ByteBuffer[]{headerBuffer, bodyBuffer}); 260 | } 261 | 262 | 263 | private ByteBuffer readFile(String path) throws IOException { 264 | path = STATIC_RESOURCE_DIR + (path.endsWith("/") ? path + INDEX_PAGE : path); 265 | RandomAccessFile raf = new RandomAccessFile(path, "r"); 266 | FileChannel channel = raf.getChannel(); 267 | 268 | ByteBuffer buffer = ByteBuffer.allocate((int) channel.size()); 269 | channel.read(buffer); 270 | 271 | buffer.flip(); 272 | return buffer; 273 | } 274 | 275 | private void log(String ip, Headers headers, int code) { 276 | // ip [date] "Method path version" code user-agent 277 | String dateStr = Date.from(Instant.now()).toString(); 278 | String msg = String.format("%s [%s] \"%s %s %s\" %d %s", 279 | ip, dateStr, headers.getMethod(), headers.getPath(), headers.getVersion(), code, headers.get("User-Agent")); 280 | System.out.println(msg); 281 | } 282 | 283 | public static void main(String[] args) throws IOException { 284 | new TinyHttpd().start(); 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/exception/InvalidHeaderException.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd.exception; 2 | 3 | /** 4 | * InvalidHeaderException 5 | * 6 | * @author coolblog.xyz 7 | * @date 2018-03-26 22:34:59 8 | */ 9 | public class InvalidHeaderException extends RuntimeException { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/json/BeautifyJsonUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd.json; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import xyz.coolblog.httpd.json.model.JsonArray; 6 | import xyz.coolblog.httpd.json.model.JsonObject; 7 | 8 | /** 9 | * Created by code4wt on 17/9/1. 10 | */ 11 | public class BeautifyJsonUtils { 12 | 13 | private static final char SPACE_CHAR = ' '; 14 | 15 | private static final int INDENT_SIZE = 2; 16 | 17 | private static int callDepth = 0; 18 | 19 | public static String beautify(JsonObject jsonObject) { 20 | 21 | StringBuilder sb = new StringBuilder(); 22 | sb.append(getIndentString()); 23 | sb.append("{"); 24 | callDepth++; 25 | 26 | List> keyValues = jsonObject.getAllKeyValue(); 27 | int size = keyValues.size(); 28 | for (int i = 0; i < size; i++) { 29 | Map.Entry keyValue = keyValues.get(i); 30 | 31 | String key = keyValue.getKey(); 32 | Object value = keyValue.getValue(); 33 | 34 | sb.append("\n"); 35 | sb.append(getIndentString()); 36 | sb.append("\""); 37 | sb.append(key); 38 | sb.append("\""); 39 | sb.append(": "); 40 | 41 | if (value instanceof JsonObject) { 42 | sb.append("\n"); 43 | sb.append(beautify((JsonObject) value)); 44 | } else if (value instanceof JsonArray){ 45 | sb.append("\n"); 46 | sb.append(beautify((JsonArray) value)); 47 | } else if (value instanceof String) { 48 | sb.append("\""); 49 | sb.append(value); 50 | sb.append("\""); 51 | } else { 52 | sb.append(value); 53 | } 54 | 55 | if (i < size - 1) { 56 | sb.append(","); 57 | } 58 | } 59 | 60 | callDepth--; 61 | sb.append("\n"); 62 | sb.append(getIndentString()); 63 | sb.append("}"); 64 | 65 | return sb.toString(); 66 | } 67 | 68 | public static String beautify(JsonArray jsonArray) { 69 | StringBuilder sb = new StringBuilder(); 70 | sb.append(getIndentString()); 71 | sb.append("["); 72 | callDepth++; 73 | 74 | int size = jsonArray.size(); 75 | for (int i = 0; i < size; i++) { 76 | 77 | sb.append("\n"); 78 | 79 | Object ele = jsonArray.get(i); 80 | if (ele instanceof JsonObject) { 81 | sb.append(beautify((JsonObject) ele)); 82 | } else if (ele instanceof JsonArray) { 83 | sb.append(beautify((JsonArray) ele)); 84 | } else if (ele instanceof String) { 85 | sb.append(getIndentString()); 86 | sb.append("\""); 87 | sb.append(ele); 88 | sb.append("\""); 89 | } else { 90 | sb.append(getIndentString()); 91 | sb.append(ele); 92 | } 93 | 94 | if (i < size - 1) { 95 | sb.append(","); 96 | } 97 | } 98 | 99 | callDepth--; 100 | sb.append("\n"); 101 | sb.append(getIndentString()); 102 | sb.append("]"); 103 | 104 | return sb.toString(); 105 | } 106 | 107 | private static String getIndentString() { 108 | StringBuilder sb = new StringBuilder(); 109 | for (int i = 0; i < callDepth * INDENT_SIZE; i++) { 110 | sb.append(SPACE_CHAR); 111 | } 112 | 113 | return sb.toString(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/json/JSONParser.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd.json; 2 | 3 | import java.io.IOException; 4 | import java.io.StringReader; 5 | import xyz.coolblog.httpd.json.parser.Parser; 6 | import xyz.coolblog.httpd.json.tokenizer.CharReader; 7 | import xyz.coolblog.httpd.json.tokenizer.TokenList; 8 | import xyz.coolblog.httpd.json.tokenizer.Tokenizer; 9 | 10 | /** 11 | * Created by code4wt on 17/9/1. 12 | */ 13 | public class JSONParser { 14 | 15 | private Tokenizer tokenizer = new Tokenizer(); 16 | 17 | private Parser parser = new Parser(); 18 | 19 | public Object fromJSON(String json) throws IOException { 20 | CharReader charReader = new CharReader(new StringReader(json)); 21 | TokenList tokens = tokenizer.tokenize(charReader); 22 | return parser.parse(tokens); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/json/exception/JsonParseException.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd.json.exception; 2 | 3 | /** 4 | * Created by code4wt on 17/5/11. 5 | */ 6 | public class JsonParseException extends RuntimeException { 7 | 8 | public JsonParseException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/json/exception/JsonTypeException.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd.json.exception; 2 | 3 | /** 4 | * Created by code4wt on 17/5/11. 5 | */ 6 | public class JsonTypeException extends RuntimeException { 7 | 8 | public JsonTypeException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/json/model/JsonArray.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd.json.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | import xyz.coolblog.httpd.json.BeautifyJsonUtils; 7 | import xyz.coolblog.httpd.json.exception.JsonTypeException; 8 | 9 | /** 10 | * Created by code4wt on 17/5/19. 11 | */ 12 | public class JsonArray implements Iterable { 13 | 14 | private List list = new ArrayList(); 15 | 16 | public void add(Object obj) { 17 | list.add(obj); 18 | } 19 | 20 | public Object get(int index) { 21 | return list.get(index); 22 | } 23 | 24 | public int size() { 25 | return list.size(); 26 | } 27 | 28 | public JsonObject getJsonObject(int index) { 29 | Object obj = list.get(index); 30 | if (!(obj instanceof JsonObject)) { 31 | throw new JsonTypeException("Type of value is not JsonObject"); 32 | } 33 | 34 | return (JsonObject) obj; 35 | } 36 | 37 | public JsonArray getJsonArray(int index) { 38 | Object obj = list.get(index); 39 | if (!(obj instanceof JsonArray)) { 40 | throw new JsonTypeException("Type of value is not JsonArray"); 41 | } 42 | 43 | return (JsonArray) obj; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return BeautifyJsonUtils.beautify(this); 49 | } 50 | 51 | @Override 52 | public Iterator iterator() { 53 | return list.iterator(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/json/model/JsonObject.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd.json.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import xyz.coolblog.httpd.json.BeautifyJsonUtils; 8 | import xyz.coolblog.httpd.json.exception.JsonTypeException; 9 | 10 | /** 11 | * Created by code4wt on 17/5/19. 12 | */ 13 | public class JsonObject { 14 | 15 | private Map map = new HashMap(); 16 | 17 | public void put(String key, Object value) { 18 | map.put(key, value); 19 | } 20 | 21 | public Object get(String key) { 22 | return map.get(key); 23 | } 24 | 25 | public List> getAllKeyValue() { 26 | return new ArrayList<>(map.entrySet()); 27 | } 28 | 29 | public JsonObject getJsonObject(String key) { 30 | if (!map.containsKey(key)) { 31 | throw new IllegalArgumentException("Invalid key"); 32 | } 33 | 34 | Object obj = map.get(key); 35 | if (!(obj instanceof JsonObject)) { 36 | throw new JsonTypeException("Type of value is not JsonObject"); 37 | } 38 | 39 | return (JsonObject) obj; 40 | } 41 | 42 | public JsonArray getJsonArray(String key) { 43 | if (!map.containsKey(key)) { 44 | throw new IllegalArgumentException("Invalid key"); 45 | } 46 | 47 | Object obj = map.get(key); 48 | if (!(obj instanceof JsonArray)) { 49 | throw new JsonTypeException("Type of value is not JsonArray"); 50 | } 51 | 52 | return (JsonArray) obj; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return BeautifyJsonUtils.beautify(this); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/json/parser/Parser.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd.json.parser; 2 | 3 | 4 | import xyz.coolblog.httpd.json.exception.JsonParseException; 5 | import xyz.coolblog.httpd.json.model.JsonArray; 6 | import xyz.coolblog.httpd.json.model.JsonObject; 7 | import xyz.coolblog.httpd.json.tokenizer.Token; 8 | import xyz.coolblog.httpd.json.tokenizer.TokenList; 9 | import xyz.coolblog.httpd.json.tokenizer.TokenType; 10 | 11 | /** 12 | * Created by code4wt on 17/5/19. 13 | */ 14 | public class Parser { 15 | 16 | private static final int BEGIN_OBJECT_TOKEN = 1; 17 | private static final int END_OBJECT_TOKEN = 2; 18 | private static final int BEGIN_ARRAY_TOKEN = 4; 19 | private static final int END_ARRAY_TOKEN = 8; 20 | private static final int NULL_TOKEN = 16; 21 | private static final int NUMBER_TOKEN = 32; 22 | private static final int STRING_TOKEN = 64; 23 | private static final int BOOLEAN_TOKEN = 128; 24 | private static final int SEP_COLON_TOKEN = 256; 25 | private static final int SEP_COMMA_TOKEN = 512; 26 | 27 | private TokenList tokens; 28 | 29 | public Object parse(TokenList tokens) { 30 | this.tokens = tokens; 31 | return parse(); 32 | } 33 | 34 | private Object parse() { 35 | Token token = tokens.next(); 36 | if (token == null) { 37 | return new JsonObject(); 38 | } else if (token.getTokenType() == TokenType.BEGIN_OBJECT) { 39 | return parseJsonObject(); 40 | } else if (token.getTokenType() == TokenType.BEGIN_ARRAY) { 41 | return parseJsonArray(); 42 | } else { 43 | throw new JsonParseException("Parse error, invalid Token."); 44 | } 45 | } 46 | 47 | private JsonObject parseJsonObject() { 48 | JsonObject jsonObject = new JsonObject(); 49 | int expectToken = STRING_TOKEN | END_OBJECT_TOKEN; 50 | String key = null; 51 | Object value = null; 52 | while (tokens.hasMore()) { 53 | Token token = tokens.next(); 54 | TokenType tokenType = token.getTokenType(); 55 | String tokenValue = token.getValue(); 56 | switch (tokenType) { 57 | case BEGIN_OBJECT: 58 | checkExpectToken(tokenType, expectToken); 59 | jsonObject.put(key, parseJsonObject()); // 递归解析 json object 60 | expectToken = SEP_COMMA_TOKEN | END_OBJECT_TOKEN; 61 | break; 62 | case END_OBJECT: 63 | checkExpectToken(tokenType, expectToken); 64 | return jsonObject; 65 | case BEGIN_ARRAY: // 解析 json array 66 | checkExpectToken(tokenType, expectToken); 67 | jsonObject.put(key, parseJsonArray()); 68 | expectToken = SEP_COMMA_TOKEN | END_OBJECT_TOKEN; 69 | break; 70 | case NULL: 71 | checkExpectToken(tokenType, expectToken); 72 | jsonObject.put(key, null); 73 | expectToken = SEP_COMMA_TOKEN | END_OBJECT_TOKEN; 74 | break; 75 | case NUMBER: 76 | checkExpectToken(tokenType, expectToken); 77 | if (tokenValue.contains(".") || tokenValue.contains("e") || tokenValue.contains("E")) { 78 | jsonObject.put(key, Double.valueOf(tokenValue)); 79 | } else { 80 | Long num = Long.valueOf(tokenValue); 81 | if (num > Integer.MAX_VALUE || num < Integer.MIN_VALUE) { 82 | jsonObject.put(key, num); 83 | } else { 84 | jsonObject.put(key, num.intValue()); 85 | } 86 | } 87 | expectToken = SEP_COMMA_TOKEN | END_OBJECT_TOKEN; 88 | break; 89 | case BOOLEAN: 90 | checkExpectToken(tokenType, expectToken); 91 | jsonObject.put(key, Boolean.valueOf(token.getValue())); 92 | expectToken = SEP_COMMA_TOKEN | END_OBJECT_TOKEN; 93 | break; 94 | case STRING: 95 | checkExpectToken(tokenType, expectToken); 96 | Token preToken = tokens.peekPrevious(); 97 | /* 98 | * 在 JSON 中,字符串既可以作为键,也可作为值。 99 | * 作为键时,只期待下一个 Token 类型为 SEP_COLON。 100 | * 作为值时,期待下一个 Token 类型为 SEP_COMMA 或 END_OBJECT 101 | */ 102 | if (preToken.getTokenType() == TokenType.SEP_COLON) { 103 | value = token.getValue(); 104 | jsonObject.put(key, value); 105 | expectToken = SEP_COMMA_TOKEN | END_OBJECT_TOKEN; 106 | } else { 107 | key = token.getValue(); 108 | expectToken = SEP_COLON_TOKEN; 109 | } 110 | break; 111 | case SEP_COLON: 112 | checkExpectToken(tokenType, expectToken); 113 | expectToken = NULL_TOKEN | NUMBER_TOKEN | BOOLEAN_TOKEN | STRING_TOKEN 114 | | BEGIN_OBJECT_TOKEN | BEGIN_ARRAY_TOKEN; 115 | break; 116 | case SEP_COMMA: 117 | checkExpectToken(tokenType, expectToken); 118 | expectToken = STRING_TOKEN; 119 | break; 120 | case END_DOCUMENT: 121 | checkExpectToken(tokenType, expectToken); 122 | return jsonObject; 123 | default: 124 | throw new JsonParseException("Unexpected Token."); 125 | } 126 | } 127 | 128 | throw new JsonParseException("Parse error, invalid Token."); 129 | } 130 | 131 | private JsonArray parseJsonArray() { 132 | int expectToken = BEGIN_ARRAY_TOKEN | END_ARRAY_TOKEN | BEGIN_OBJECT_TOKEN | NULL_TOKEN 133 | | NUMBER_TOKEN | BOOLEAN_TOKEN | STRING_TOKEN; 134 | JsonArray jsonArray = new JsonArray(); 135 | while (tokens.hasMore()) { 136 | Token token = tokens.next(); 137 | TokenType tokenType = token.getTokenType(); 138 | String tokenValue = token.getValue(); 139 | switch (tokenType) { 140 | case BEGIN_OBJECT: 141 | checkExpectToken(tokenType, expectToken); 142 | jsonArray.add(parseJsonObject()); 143 | expectToken = SEP_COMMA_TOKEN | END_ARRAY_TOKEN; 144 | break; 145 | case BEGIN_ARRAY: 146 | checkExpectToken(tokenType, expectToken); 147 | jsonArray.add(parseJsonArray()); 148 | expectToken = SEP_COMMA_TOKEN | END_ARRAY_TOKEN; 149 | break; 150 | case END_ARRAY: 151 | checkExpectToken(tokenType, expectToken); 152 | return jsonArray; 153 | case NULL: 154 | checkExpectToken(tokenType, expectToken); 155 | jsonArray.add(null); 156 | expectToken = SEP_COMMA_TOKEN | END_ARRAY_TOKEN; 157 | break; 158 | case NUMBER: 159 | checkExpectToken(tokenType, expectToken); 160 | if (tokenValue.contains(".") || tokenValue.contains("e") || tokenValue.contains("E")) { 161 | jsonArray.add(Double.valueOf(tokenValue)); 162 | } else { 163 | Long num = Long.valueOf(tokenValue); 164 | if (num > Integer.MAX_VALUE || num < Integer.MIN_VALUE) { 165 | jsonArray.add(num); 166 | } else { 167 | jsonArray.add(num.intValue()); 168 | } 169 | } 170 | expectToken = SEP_COMMA_TOKEN | END_ARRAY_TOKEN; 171 | break; 172 | case BOOLEAN: 173 | checkExpectToken(tokenType, expectToken); 174 | jsonArray.add(Boolean.valueOf(tokenValue)); 175 | expectToken = SEP_COMMA_TOKEN | END_ARRAY_TOKEN; 176 | break; 177 | case STRING: 178 | checkExpectToken(tokenType, expectToken); 179 | jsonArray.add(tokenValue); 180 | expectToken = SEP_COMMA_TOKEN | END_ARRAY_TOKEN; 181 | break; 182 | case SEP_COMMA: 183 | checkExpectToken(tokenType, expectToken); 184 | expectToken = STRING_TOKEN | NULL_TOKEN | NUMBER_TOKEN | BOOLEAN_TOKEN 185 | | BEGIN_ARRAY_TOKEN | BEGIN_OBJECT_TOKEN; 186 | break; 187 | case END_DOCUMENT: 188 | checkExpectToken(tokenType, expectToken); 189 | return jsonArray; 190 | default: 191 | throw new JsonParseException("Unexpected Token."); 192 | } 193 | } 194 | 195 | throw new JsonParseException("Parse error, invalid Token."); 196 | } 197 | 198 | private void checkExpectToken(TokenType tokenType, int expectToken) { 199 | if ((tokenType.getTokenCode() & expectToken) == 0) { 200 | throw new JsonParseException("Parse error, invalid Token."); 201 | } 202 | } 203 | } -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/json/tokenizer/CharReader.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd.json.tokenizer; 2 | 3 | import java.io.IOException; 4 | import java.io.Reader; 5 | 6 | /** 7 | * Created by code4wt on 17/5/11. 8 | */ 9 | public class CharReader { 10 | 11 | private static final int BUFFER_SIZE = 1024; 12 | 13 | private Reader reader; 14 | 15 | private char[] buffer; 16 | 17 | private int pos; 18 | 19 | private int size; 20 | 21 | public CharReader(Reader reader) { 22 | this.reader = reader; 23 | buffer = new char[BUFFER_SIZE]; 24 | } 25 | 26 | /** 27 | * 返回 pos 下标处的字符,并返回 28 | * @return 29 | * @throws IOException 30 | */ 31 | public char peek() throws IOException { 32 | if (pos - 1 >= size) { 33 | return (char) -1; 34 | } 35 | 36 | return buffer[Math.max(0, pos - 1)]; 37 | } 38 | 39 | /** 40 | * 返回 pos 下标处的字符,并将 pos + 1,最后返回字符 41 | * @return 42 | * @throws IOException 43 | */ 44 | public char next() throws IOException { 45 | if (!hasMore()) { 46 | return (char) -1; 47 | } 48 | 49 | return buffer[pos++]; 50 | } 51 | 52 | public void back() { 53 | pos = Math.max(0, --pos); 54 | } 55 | 56 | public boolean hasMore() throws IOException { 57 | if (pos < size) { 58 | return true; 59 | } 60 | 61 | fillBuffer(); 62 | return pos < size; 63 | } 64 | 65 | void fillBuffer() throws IOException { 66 | int n = reader.read(buffer); 67 | if (n == -1) { 68 | return; 69 | } 70 | 71 | pos = 0; 72 | size = n; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/json/tokenizer/Token.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd.json.tokenizer; 2 | 3 | /** 4 | * Created by code4wt on 17/5/10. 5 | */ 6 | public class Token { 7 | 8 | private TokenType tokenType; 9 | 10 | private String value; 11 | 12 | public Token(TokenType tokenType, String value) { 13 | this.tokenType = tokenType; 14 | this.value = value; 15 | } 16 | 17 | public TokenType getTokenType() { 18 | return tokenType; 19 | } 20 | 21 | public void setTokenType(TokenType tokenType) { 22 | this.tokenType = tokenType; 23 | } 24 | 25 | public String getValue() { 26 | return value; 27 | } 28 | 29 | public void setValue(String value) { 30 | this.value = value; 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return "Token{" + 36 | "tokenType=" + tokenType + 37 | ", value='" + value + '\'' + 38 | '}'; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/json/tokenizer/TokenList.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd.json.tokenizer; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by code4wt on 17/5/19. 8 | */ 9 | public class TokenList { 10 | 11 | private List tokens = new ArrayList(); 12 | 13 | private int pos = 0; 14 | 15 | public void add(Token token) { 16 | tokens.add(token); 17 | } 18 | 19 | public Token peek() { 20 | return pos < tokens.size() ? tokens.get(pos) : null; 21 | } 22 | 23 | public Token peekPrevious() { 24 | return pos - 1 < 0 ? null : tokens.get(pos - 2); 25 | } 26 | 27 | public Token next() { 28 | return tokens.get(pos++); 29 | } 30 | 31 | public boolean hasMore() { 32 | return pos < tokens.size(); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "TokenList{" + 38 | "tokens=" + tokens + 39 | '}'; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/json/tokenizer/TokenType.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd.json.tokenizer; 2 | 3 | /** 4 | * Created by code4wt on 17/5/10. 5 | */ 6 | public enum TokenType { 7 | BEGIN_OBJECT(1), 8 | END_OBJECT(2), 9 | BEGIN_ARRAY(4), 10 | END_ARRAY(8), 11 | NULL(16), 12 | NUMBER(32), 13 | STRING(64), 14 | BOOLEAN(128), 15 | SEP_COLON(256), 16 | SEP_COMMA(512), 17 | END_DOCUMENT(1024); 18 | 19 | TokenType(int code) { 20 | this.code = code; 21 | } 22 | 23 | private int code; 24 | 25 | public int getTokenCode() { 26 | return code; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /java/xyz/coolblog/httpd/json/tokenizer/Tokenizer.java: -------------------------------------------------------------------------------- 1 | package xyz.coolblog.httpd.json.tokenizer; 2 | 3 | import java.io.IOException; 4 | import xyz.coolblog.httpd.json.exception.JsonParseException; 5 | 6 | /** 7 | * Created by code4wt on 17/5/10. 8 | */ 9 | public class Tokenizer { 10 | 11 | private CharReader charReader; 12 | 13 | private TokenList tokens; 14 | 15 | public TokenList tokenize(CharReader charReader) throws IOException { 16 | this.charReader = charReader; 17 | tokens = new TokenList(); 18 | tokenize(); 19 | 20 | return tokens; 21 | } 22 | 23 | private void tokenize() throws IOException { 24 | // 使用do-while处理空文件 25 | Token token; 26 | do { 27 | token = start(); 28 | tokens.add(token); 29 | } while (token.getTokenType() != TokenType.END_DOCUMENT); 30 | } 31 | 32 | private Token start() throws IOException { 33 | char ch; 34 | for(;;) { 35 | if (!charReader.hasMore()) { 36 | return new Token(TokenType.END_DOCUMENT, null); 37 | } 38 | 39 | ch = charReader.next(); 40 | if (!isWhiteSpace(ch)) { 41 | break; 42 | } 43 | } 44 | 45 | switch (ch) { 46 | case '{': 47 | return new Token(TokenType.BEGIN_OBJECT, String.valueOf(ch)); 48 | case '}': 49 | return new Token(TokenType.END_OBJECT, String.valueOf(ch)); 50 | case '[': 51 | return new Token(TokenType.BEGIN_ARRAY, String.valueOf(ch)); 52 | case ']': 53 | return new Token(TokenType.END_ARRAY, String.valueOf(ch)); 54 | case ',': 55 | return new Token(TokenType.SEP_COMMA, String.valueOf(ch)); 56 | case ':': 57 | return new Token(TokenType.SEP_COLON, String.valueOf(ch)); 58 | case 'n': 59 | return readNull(); 60 | case 't': 61 | case 'f': 62 | return readBoolean(); 63 | case '"': 64 | return readString(); 65 | case '-': 66 | return readNumber(); 67 | } 68 | 69 | if (isDigit(ch)) { 70 | return readNumber(); 71 | } 72 | 73 | throw new JsonParseException("Illegal character"); 74 | } 75 | 76 | private boolean isWhiteSpace(char ch) { 77 | return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); 78 | } 79 | 80 | private Token readString() throws IOException { 81 | StringBuilder sb = new StringBuilder(); 82 | for (;;) { 83 | char ch = charReader.next(); 84 | if (ch == '\\') { 85 | if (!isEscape()) { 86 | throw new JsonParseException("Invalid escape character"); 87 | } 88 | sb.append('\\'); 89 | ch = charReader.peek(); 90 | sb.append(ch); 91 | if (ch == 'u') { 92 | for (int i = 0; i < 4; i++) { 93 | ch = charReader.next(); 94 | if (isHex(ch)) { 95 | sb.append(ch); 96 | } else { 97 | throw new JsonParseException("Invalid character"); 98 | } 99 | } 100 | } 101 | } else if (ch == '"') { 102 | return new Token(TokenType.STRING, sb.toString()); 103 | } else if (ch == '\r' || ch == '\n') { 104 | throw new JsonParseException("Invalid character"); 105 | } else { 106 | sb.append(ch); 107 | } 108 | } 109 | } 110 | 111 | private boolean isEscape() throws IOException { 112 | char ch = charReader.next(); 113 | return (ch == '"' || ch == '\\' || ch == 'u' || ch == 'r' 114 | || ch == 'n' || ch == 'b' || ch == 't' || ch == 'f'); 115 | 116 | } 117 | 118 | private boolean isHex(char ch) { 119 | return ((ch >= '0' && ch <= '9') || ('a' <= ch && ch <= 'f') 120 | || ('A' <= ch && ch <= 'F')); 121 | } 122 | 123 | private Token readNumber() throws IOException { 124 | char ch = charReader.peek(); 125 | StringBuilder sb = new StringBuilder(); 126 | if (ch == '-') { // 处理负数 127 | sb.append(ch); 128 | ch = charReader.next(); 129 | if (ch == '0') { // 处理 -0.xxxx 130 | sb.append(ch); 131 | sb.append(readFracAndExp()); 132 | } else if (isDigitOne2Nine(ch)) { 133 | do { 134 | sb.append(ch); 135 | ch = charReader.next(); 136 | } while (isDigit(ch)); 137 | if (ch != (char) -1) { 138 | charReader.back(); 139 | sb.append(readFracAndExp()); 140 | } 141 | } else { 142 | throw new JsonParseException("Invalid minus number"); 143 | } 144 | } else if (ch == '0') { // 处理小数 145 | sb.append(ch); 146 | sb.append(readFracAndExp()); 147 | } else { 148 | do { 149 | sb.append(ch); 150 | ch = charReader.next(); 151 | } while (isDigit(ch)); 152 | if (ch != (char) -1) { 153 | charReader.back(); 154 | sb.append(readFracAndExp()); 155 | } 156 | } 157 | 158 | return new Token(TokenType.NUMBER, sb.toString()); 159 | } 160 | 161 | private boolean isExp(char ch) throws IOException { 162 | return ch == 'e' || ch == 'E'; 163 | } 164 | 165 | private boolean isDigit(char ch) { 166 | return ch >= '0' && ch <= '9'; 167 | } 168 | 169 | private boolean isDigitOne2Nine(char ch) { 170 | return ch >= '0' && ch <= '9'; 171 | } 172 | 173 | private String readFracAndExp() throws IOException { 174 | StringBuilder sb = new StringBuilder(); 175 | char ch = charReader.next(); 176 | if (ch == '.') { 177 | sb.append(ch); 178 | ch = charReader.next(); 179 | if (!isDigit(ch)) { 180 | throw new JsonParseException("Invalid frac"); 181 | } 182 | do { 183 | sb.append(ch); 184 | ch = charReader.next(); 185 | } while (isDigit(ch)); 186 | 187 | if (isExp(ch)) { // 处理科学计数法 188 | sb.append(ch); 189 | sb.append(readExp()); 190 | } else { 191 | if (ch != (char) -1) { 192 | charReader.back(); 193 | } 194 | } 195 | } else if (isExp(ch)) { 196 | sb.append(ch); 197 | sb.append(readExp()); 198 | } else { 199 | charReader.back(); 200 | } 201 | 202 | return sb.toString(); 203 | } 204 | 205 | private String readExp() throws IOException { 206 | StringBuilder sb = new StringBuilder(); 207 | char ch = charReader.next(); 208 | if (ch == '+' || ch =='-') { 209 | sb.append(ch); 210 | ch = charReader.next(); 211 | if (isDigit(ch)) { 212 | do { 213 | sb.append(ch); 214 | ch = charReader.next(); 215 | } while (isDigit(ch)); 216 | 217 | if (ch != (char) -1) { // 读取结束,不用回退 218 | charReader.back(); 219 | } 220 | } else { 221 | throw new JsonParseException("e or E"); 222 | } 223 | } else { 224 | throw new JsonParseException("e or E"); 225 | } 226 | 227 | return sb.toString(); 228 | } 229 | 230 | private Token readBoolean() throws IOException { 231 | if (charReader.peek() == 't') { 232 | if (!(charReader.next() == 'r' && charReader.next() == 'u' && charReader.next() == 'e')) { 233 | throw new JsonParseException("Invalid json string"); 234 | } 235 | 236 | return new Token(TokenType.BOOLEAN, "true"); 237 | } else { 238 | if (!(charReader.next() == 'a' && charReader.next() == 'l' 239 | && charReader.next() == 's' && charReader.next() == 'e')) { 240 | throw new JsonParseException("Invalid json string"); 241 | } 242 | 243 | return new Token(TokenType.BOOLEAN, "false"); 244 | } 245 | } 246 | 247 | private Token readNull() throws IOException { 248 | if (!(charReader.next() == 'u' && charReader.next() == 'l' && charReader.next() == 'l')) { 249 | throw new JsonParseException("Invalid json string"); 250 | } 251 | 252 | return new Token(TokenType.NULL, "null"); 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /multiprocess_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "httpd.h" 10 | 11 | #define BUFFER_SIZE 2048 12 | #define DEFAULT_PORT 8080 13 | 14 | void handle_subprocess_exit(int); 15 | 16 | int main(int argc, char *argv[]) 17 | { 18 | struct sockaddr_in server_addr, client_addr; 19 | int client_addr_size; 20 | int listen_fd, conn_fd; 21 | int on; 22 | 23 | client_addr_size = sizeof(client_addr); 24 | listen_fd = socket(AF_INET, SOCK_STREAM, 0); 25 | setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 26 | 27 | bzero(&server_addr, sizeof(server_addr)); 28 | server_addr.sin_family = AF_INET; 29 | server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 30 | server_addr.sin_port = htons(DEFAULT_PORT); 31 | 32 | if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { 33 | perror("bind error, message: "); 34 | exit(1); 35 | } 36 | 37 | if (listen(listen_fd, 5) == -1) { 38 | perror("listen error, message: "); 39 | exit(1); 40 | } 41 | 42 | printf("listening 8080\n"); 43 | 44 | signal(SIGCHLD, handle_subprocess_exit); 45 | 46 | while (1) { 47 | conn_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &client_addr_size); 48 | if (conn_fd == -1) { 49 | perror("accept error, message: "); 50 | continue; 51 | } 52 | 53 | pid_t pid = fork(); 54 | if (pid == 0) { 55 | close(listen_fd); 56 | accept_request(conn_fd, &client_addr); 57 | close(conn_fd); 58 | exit(0); 59 | } 60 | 61 | close(conn_fd); 62 | } 63 | 64 | return 0; 65 | } 66 | 67 | void handle_subprocess_exit(int signo) { 68 | int status; 69 | while(waitpid(-1, &status, WNOHANG) > 0); 70 | } -------------------------------------------------------------------------------- /poll_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "httpd.h" 10 | 11 | #define BUFFER_SIZE 2048 12 | #define DEFAULT_PORT 8080 13 | #define INFTIM -1 14 | #define OPEN_MAX 1024 15 | 16 | int main(int argc, char *argv[]) 17 | { 18 | struct sockaddr_in server_addr, client_addr; 19 | int client_addr_size; 20 | int listen_fd, conn_fd; 21 | struct pollfd client_fds[OPEN_MAX]; 22 | int index, max_index; 23 | int ready_fd_num; 24 | int on = 1; 25 | 26 | client_addr_size = sizeof(client_addr); 27 | listen_fd = socket(AF_INET, SOCK_STREAM, 0); 28 | setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 29 | 30 | bzero(&server_addr, sizeof(server_addr)); 31 | server_addr.sin_family = AF_INET; 32 | server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 33 | server_addr.sin_port = htons(DEFAULT_PORT); 34 | 35 | if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { 36 | perror("bind error, message: "); 37 | exit(1); 38 | } 39 | 40 | if (listen(listen_fd, 5) == -1) { 41 | perror("listen error, message: "); 42 | exit(1); 43 | } 44 | 45 | printf("listening 8080\n"); 46 | 47 | for (int i = 0; i < OPEN_MAX; i++) { 48 | client_fds[i].fd = -1; 49 | } 50 | max_index = 0; 51 | 52 | client_fds[0].fd = listen_fd; 53 | client_fds[0].events = POLLRDNORM; 54 | 55 | while (1) { 56 | ready_fd_num = poll(client_fds, max_index + 1, INFTIM); 57 | if (ready_fd_num < 0) { 58 | perror("poll error, message: "); 59 | continue; 60 | } 61 | 62 | if (client_fds[0].revents & POLLRDNORM) { 63 | conn_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &client_addr_size); 64 | if (conn_fd == -1) { 65 | perror("accept error, message: "); 66 | continue; 67 | } 68 | 69 | for (index = 1; index < OPEN_MAX; index++) { 70 | if (client_fds[index].fd < 0) { 71 | client_fds[index].fd = conn_fd; 72 | client_fds[index].events = POLLRDNORM; 73 | break; 74 | } 75 | } 76 | 77 | if (index == OPEN_MAX) { 78 | fprintf(stderr, "too many connections\n"); 79 | close(conn_fd); 80 | continue; 81 | } 82 | 83 | if (index > max_index) { 84 | max_index = index; 85 | } 86 | 87 | if (--ready_fd_num <= 0) { 88 | continue; 89 | } 90 | } 91 | 92 | for (int i = 1; i <= max_index; i++) { 93 | if ((conn_fd = client_fds[i].fd) < 0) { 94 | continue; 95 | } 96 | 97 | if (client_fds[i].revents & POLLRDNORM) { 98 | accept_request(client_fds[i].fd, &client_addr); 99 | close(client_fds[i].fd); 100 | client_fds[i].fd = -1; 101 | } else { 102 | close(client_fds[i].fd); 103 | client_fds[i].fd = -1; 104 | } 105 | 106 | if (--ready_fd_num <= 0) { 107 | break; 108 | } 109 | } 110 | } 111 | 112 | return 0; 113 | } -------------------------------------------------------------------------------- /select_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "httpd.h" 9 | 10 | #define BUFFER_SIZE 2048 11 | #define DEFAULT_PORT 8080 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | struct sockaddr_in server_addr, client_addr; 16 | int client_addr_size; 17 | int listen_fd, conn_fd, max_fd; 18 | int client_fds[FD_SETSIZE]; 19 | int index, max_index; 20 | fd_set read_set, all_set; 21 | int ready_fd_num; 22 | int on = 1; 23 | 24 | client_addr_size = sizeof(client_addr); 25 | listen_fd = socket(AF_INET, SOCK_STREAM, 0); 26 | setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 27 | 28 | bzero(&server_addr, sizeof(server_addr)); 29 | server_addr.sin_family = AF_INET; 30 | server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 31 | server_addr.sin_port = htons(DEFAULT_PORT); 32 | 33 | if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { 34 | perror("bind error, message: "); 35 | exit(1); 36 | } 37 | 38 | if (listen(listen_fd, 5) == -1) { 39 | perror("listen error, message: "); 40 | exit(1); 41 | } 42 | 43 | printf("listening 8080\n"); 44 | 45 | for (int i = 0; i < FD_SETSIZE; i++) { 46 | client_fds[i] = -1; 47 | } 48 | max_index = 0; 49 | 50 | FD_ZERO(&read_set); 51 | FD_ZERO(&all_set); 52 | FD_SET(listen_fd, &all_set); 53 | max_fd = listen_fd; 54 | 55 | while (1) { 56 | read_set = all_set; 57 | ready_fd_num = select(max_fd + 1, &read_set, NULL, NULL, NULL); 58 | if (ready_fd_num < 0) { 59 | perror("select error, message: "); 60 | continue; 61 | } 62 | 63 | if (FD_ISSET(listen_fd, &read_set)) { 64 | conn_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &client_addr_size); 65 | if (conn_fd == -1) { 66 | perror("accept error, message: "); 67 | continue; 68 | } 69 | 70 | for (index = 1; index < FD_SETSIZE; index++) { 71 | if (client_fds[index] < 0) { 72 | client_fds[index] = conn_fd; 73 | break; 74 | } 75 | } 76 | 77 | if (index == FD_SETSIZE) { 78 | fprintf(stderr, "too many connections\n"); 79 | close(conn_fd); 80 | continue; 81 | } 82 | 83 | if (index > max_index) { 84 | max_index = index; 85 | } 86 | 87 | FD_SET(conn_fd, &all_set); 88 | if (conn_fd > max_fd) { 89 | max_fd = conn_fd; 90 | } 91 | 92 | if (--ready_fd_num <= 0) { 93 | continue; 94 | } 95 | } 96 | 97 | for (int i = 1; i <= max_index; i++) { 98 | conn_fd = client_fds[i]; 99 | if (conn_fd == -1) { 100 | continue; 101 | } 102 | 103 | if (FD_ISSET(conn_fd, &read_set)) { 104 | accept_request(conn_fd, &client_addr); 105 | FD_CLR(conn_fd, &all_set); 106 | close(conn_fd); 107 | client_fds[i] = -1; 108 | } 109 | 110 | if (--ready_fd_num <= 0) { 111 | break; 112 | } 113 | } 114 | } 115 | 116 | return 0; 117 | } -------------------------------------------------------------------------------- /single_process_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "httpd.h" 8 | 9 | #define BUFFER_SIZE 2048 10 | #define DEFAULT_PORT 8080 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | struct sockaddr_in server_addr, client_addr; 15 | int client_addr_size; 16 | int listen_fd, conn_fd; 17 | int on = 1; 18 | 19 | client_addr_size = sizeof(client_addr); 20 | listen_fd = socket(AF_INET, SOCK_STREAM, 0); 21 | setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 22 | 23 | bzero(&server_addr, sizeof(server_addr)); 24 | server_addr.sin_family = AF_INET; 25 | server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 26 | server_addr.sin_port = htons(DEFAULT_PORT); 27 | 28 | if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { 29 | perror("bind error, message: "); 30 | exit(1); 31 | } 32 | 33 | if (listen(listen_fd, 5) == -1) { 34 | perror("listen error, message: "); 35 | exit(1); 36 | } 37 | 38 | printf("listening 8080\n"); 39 | 40 | while (1) { 41 | conn_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &client_addr_size); 42 | if (conn_fd == -1) { 43 | perror("accept error, message: "); 44 | continue; 45 | } 46 | 47 | accept_request(conn_fd, &client_addr); 48 | close(conn_fd); 49 | } 50 | 51 | return 0; 52 | } --------------------------------------------------------------------------------