diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..fa0086a --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f83ffd0 --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +## 简介 +这是一个简单的 http 服务器 +运行于 windows 环境下 +使用 C 语言写成 +## 编译 +1.请确保你的电脑已安装好 MinGW ,并且已经把 MinGW 加入了环境变量 +2.运行 build.bat 的批处理文件 +## 如何使用 +编译完成后,双击生成的exe文件即可 +程序会占用2047端口,程序所在的目录会作为网站的根目录 +## cgi 脚本 +这个程序支持以下五种后缀名的 cgi 脚本,其中cgi后缀的是可执行文件 +``` +".cgi", ".pl", ".py", ".php", ".rb" +``` +使用 cgi 脚本时,请确保文件的第一行以这种格式说明脚本的解释器路径 +``` +#!C:\Program Files\language\Python36\python.exe +``` +如果脚本的解释器已加入环境变量也可以写成这样 +``` +#!python +``` +cgi 脚本输出的第一行需要指定响应头中的Content-Type,例如这样 +``` +Content-Type: text/html; charset=utf-8 +``` +## 目录结构 +``` +├─target 生成目录 +│ ├─test.py 测试用的cgi脚本 +│ ├─test.php 测试用的cgi脚本 +│ ├─test.cgi 测试用的cgi可执行文件 +│ ├─index.html 测试用的html文件 +├─src 当前版本源码 +│ ├─http.c 程序入口 +│ ├─parser.c 解释 http 请求报文 +│ ├─linklist.c 链表 +│ ├─test.c test.cgi的源码 +├─build.bat 执行makefile的脚本 +├─clean.bat 删除生成的文件 +├─makefile +``` +## 这个程序编写的大致思路 +1.tpc的套路,socket,bind,listen,accept +2.使用 select 模型 +3.在接收到 http 请求后解释 http 请求 +4.通过文件后缀判断是动态请求还是静态请求 +5.通过 cgi 脚本的首行获取解释器的路径 +6.难点在于解释 http 请求和执行 cgi 脚本是的输入输出重定向 +## TODO +1.解码 url 编码 +2.确保,http 进程退出之后,cgi 进程也跟着退出 +3.使用子进程 +4.cgi 请求使用 chunked +5.使用配置文件加载配置 \ No newline at end of file diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..bd74bd3 --- /dev/null +++ b/build.bat @@ -0,0 +1 @@ +mingw32-make & pause \ No newline at end of file diff --git a/clean.bat b/clean.bat new file mode 100644 index 0000000..7fcb127 --- /dev/null +++ b/clean.bat @@ -0,0 +1 @@ +mingw32-make clean & pause \ No newline at end of file diff --git a/makefile b/makefile new file mode 100644 index 0000000..09354a5 --- /dev/null +++ b/makefile @@ -0,0 +1,28 @@ +cc = gcc +project = http +count = +mainsrc = $(project)$(count).c +executable = $(project)$(count).exe +dynamiclib = -lwsock32 -lws2_32 +target = target/ +src = src/ + +all: $(executable) + +$(executable): $(src)$(mainsrc) linklist.o parser.o + $(cc) $(src)$(mainsrc) $(target)linklist.o $(target)parser.o -o $(target)$(executable) $(dynamiclib) + +linklist.o: $(src)linklist.c + $(cc) -c $(src)linklist.c -o $(target)linklist.o + +parser.o: $(src)parser.c + $(cc) -c $(src)parser.c -o $(target)parser.o + +clean: + del $(target)*.o $(executable) + +clean-exe: + del $(target)$(executable) + +exe: + $(target)$(executable) \ No newline at end of file diff --git a/src/http.c b/src/http.c new file mode 100644 index 0000000..41ffd22 --- /dev/null +++ b/src/http.c @@ -0,0 +1,504 @@ +#undef UNICODE +#define FD_SETSIZE 128 + +#include +#include +#include +#include +#include + +#include "linklist.h" +#include "parser.h" + +#pragma comment (lib, "ws2_32.lib") + + +#define CONNECT_NUM_MAX FD_SETSIZE +#define MAX_BUF 8192 + +void close_socket(fd_set *socketSet, SOCKET fd_arr) +{ + printf("close:%d\n", fd_arr); + closesocket(fd_arr); + // fd_arr = INVALID_SOCKET; + FD_CLR(fd_arr, socketSet); +} + +char tohex(int n) +{ + + if (n >= 10 && n <= 15) + { + return 'A' + n - 10; + } + return '0' + n; +} +void dec2hex(int n, char *buf) +{ + int i = 0; + int mod; + while (n) + { + mod = n % 16; + buf[i++] = tohex(mod); + n = n / 16; + } + //得进行反序。 + int j, k; + for (j = 0, k = i - 1; j < i / 2; j++, k--) + { + char temp; + temp = buf[j]; + buf[j] = buf[k]; + buf[k] = temp; + } + buf[i] = '\0'; +} + +int main(int argc, char *argv[]) +{ + + int iResult = 0; + SetConsoleOutputCP(65001); + /* + * windows下使用socket必须用WSAStartup初始化,否则不能调用 + */ + WSADATA wsaData; + iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) // 启动winsock,指定winsock库的主板本和副版本 + { + printf("WSAStartup failed: %d\n", iResult); + return 1; + } + + printf("wVersion: %d.%d\n", LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion)); + + // 创建socket套接字 + SOCKET sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd == INVALID_SOCKET) + { + printf("Error at socket(): %ld\n", WSAGetLastError()); + WSACleanup(); + return 1; + } + + // 初始化套接字 + struct sockaddr_in sockAddr; + memset(&sockAddr, 0, sizeof(sockAddr)); // 每个字节都用0填充 + sockAddr.sin_family = PF_INET; // 使用IPv4地址 + sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 具体的IP地址 + sockAddr.sin_port = htons(2047); // 端口 + // 绑定 + iResult = bind(sockfd, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)); + if (iResult == SOCKET_ERROR) + { + printf("bind function failed with error %d\n", WSAGetLastError()); + iResult = closesocket(sockfd); + if (iResult == SOCKET_ERROR) + { + printf("closesocket function failed with error %d\n", WSAGetLastError()); + } + WSACleanup(); + return 1; + } + // 监听 + iResult = listen(sockfd, CONNECT_NUM_MAX); // 设置最大连接数 + if (iResult == SOCKET_ERROR) + { + printf("listen function failed with error: %d\n", WSAGetLastError()); + } + + /* + 为了方便使用,windows sockets提供了下列宏,用来对fd_set进行一系列操作。使用以下宏可以使编程工作简化。 + FD_CLR(s,*set);从set集合中删除s套接字。 + FD_ISSET(s,*set);检查s是否为set集合的成员。 + FD_SET(s,*set);将套接字加入到set集合中。 + FD_ZERO(*set);将set集合初始化为空集合。 + */ + + // 1)初始化一个套接字集合fdSocket,并将监听套接字放入 + fd_set socketSet; + FD_ZERO(&socketSet); + FD_SET(sockfd, &socketSet); + + fd_set readSet; + fd_set writeSet; + + TIMEVAL time = {1, 0}; + + printf("FD_SETSIZE=%d\n", FD_SETSIZE); + + char buf[MAX_BUF]; + char sendBuf[MAX_BUF]; + + while (1) + { + // 2)将fdSocket的一个拷贝fdRead传给select函数 + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + // FD_SET(sockfd, &readSet); + // FD_SET(sockfd, &writeSet); + readSet = socketSet; + writeSet = socketSet; + + // 同时检查套接字的可读可写性。 + int nRetAll = select(0, &readSet, &writeSet, NULL, &time); // 若不设置超时则select为阻塞 + if (nRetAll >0) + { + // 是否存在客户端的连接请求。 + if (FD_ISSET(sockfd ,&readSet)) // 在readset中会返回已经调用过listen的套接字。 + { + + if (socketSet.fd_count < FD_SETSIZE) + { + SOCKADDR_IN addrRemote; + int nAddrLen = sizeof(addrRemote); + SOCKET sClient = accept(sockfd, (SOCKADDR*)&addrRemote, &nAddrLen); + if (sClient != INVALID_SOCKET) + { + FD_SET(sClient, &socketSet); + printf("接收到连接:(%s)\n",inet_ntoa(addrRemote.sin_addr)); + printf("a client %d connect.\t\n", sClient); + // sprintf(sendBuf, "Welcome %s", inet_ntoa(addrRemote.sin_addr)); + // send(sClient, sendBuf, strlen(sendBuf) + 1, 0); + FD_CLR(sockfd, &readSet); + } + } + else + { + printf("连接数量已达上限!\n"); + continue; + } + } + + for (int i = 0; i < readSet.fd_count; i++) + { + if (FD_ISSET(readSet.fd_array[i], &readSet)) + { + int sock_flg = 0; + int sock_i; + for (int j = 0; j < socketSet.fd_count; j++) + { + if (readSet.fd_array[i] == socketSet.fd_array[j]) + { + sock_i = j; + sock_flg = 1; + break; + } + } + if (sock_flg == 0) + { + break; + } + //调用recv,接收数据。 + int nRecv = recv(readSet.fd_array[i], buf, MAX_BUF, 0); + if (nRecv > 0) + { + buf[nRecv] = 0; + + Request *request = requset_init(); + parser_request(request, buf); + request_print(request); + + char response[8192]; + char header[2048]; + char length[4]; + char html[1024]; + char *cgi_script[] = {".cgi", ".pl", ".py", ".php", ".rb"}; + char *ext; + char temp_path[128]; + copy_cahr(request->path, temp_path, 1, strlen(request->path) + 1); + ext = is_cgi_script(temp_path, cgi_script, (sizeof(cgi_script)/sizeof(cgi_script[0]))); + + // 动态请求 + if (ext != NULL) + { + printf("ext=%s\n", ext); + char cstrNewDosCmd[MAX_PATH]; + memset(cstrNewDosCmd, 0, sizeof(char)*MAX_PATH); + + strcpy(header, "HTTP/1.1 500 Internal Server Error\r\nServer: plusplus123/0.1\r\nContent-Type: text/html;charset=UTF-8\r\nContent-Length: "); + strcpy(html, "500

500 Internal Server Error

body_raw=%s\n", request->body_raw); + if (strcmp(request->method, "POST") == 0 && request->body_raw != NULL) + { + // 用WriteFile,把post数据写入管道 + DWORD dwWritten; + if (!WriteFile(hRead, request->body_raw, strlen(request->body_raw), &dwWritten, NULL)) + { + printf("管道创建失败1\n"); + printf("error=%d\n", GetLastError()); + sprintf(length, "%d", strlen(html)); + strcpy(response, header); + strcat(response, length); + strcat(response, "\r\n\r\n"); + strcat(response, html); + send(readSet.fd_array[i], response, strlen(response) + 1, 0); + goto finish; + } + } + else + { + si.hStdInput = hWrite; + } + + si.wShowWindow = SW_HIDE; + + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + + PROCESS_INFORMATION pi; + + // 设置环境变量 + ListHead *my_envp = request->header; + link_add(my_envp, "SYSTEMROOT", getenv("SYSTEMROOT")); + link_add(my_envp, "COMSPEC", getenv("COMSPEC")); + link_add(my_envp, "PATH", getenv("PATH")); + link_add(my_envp, "PATHEXT", getenv("PATHEXT")); + link_add(my_envp, "WINDIR", getenv("WINDIR")); + link_add(my_envp, "GATEWAY_INTERFACE", "CGI/1.1"); + link_add(my_envp, "REMOTE_ADDR", request->ip); + link_add(my_envp, "SCRIPT_NAME", request->path); + link_add(my_envp, "REQUEST_URI", request->request_uri); + link_add(my_envp, "QUERY_STRING", request->query_string); + link_add(my_envp, "REQUEST_METHOD", request->method); + link_add(my_envp, "SERVER_PROTOCOL", request->http_version); + + char temp_env[16384]; + char my_envp_item_temp[4096]; + int i5 = 0; + int temp_env_len = 0; + ListStruct *my_envp_item; + my_envp_item = my_envp->first; + int start1 = 0; + do + { + strcpy(my_envp_item_temp, my_envp_item->name); + strcat(my_envp_item_temp, "="); + strcat(my_envp_item_temp, my_envp_item->value); + int my_env_item_len = strlen(my_envp_item_temp); + start1 = temp_env_len; + for (int i4 = 0; i4 < my_env_item_len; i4++) + { + temp_env[start1] = my_envp_item_temp[i4]; + start1++; + } + temp_env_len += my_env_item_len; + temp_env[temp_env_len] = '\0'; + temp_env_len++; + my_envp_item = my_envp_item->next; + } while (my_envp_item != NULL); + temp_env[temp_env_len] = '\0'; + + // 启动进程 + DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW; + BOOL bSuc = CreateProcessA(NULL, cstrNewDosCmd, NULL, NULL, TRUE, dwCreationFlag, (LPVOID)temp_env, NULL, &si, &pi); + if (bSuc == 0) + { + DWORD err = GetLastError(); + printf("cgi启动失败 "); + printf("error=%d\n", err); + + printf("cstrNewDosCmd=%s\n", cstrNewDosCmd); + printf("temp_env=\n"); + link_print(my_envp); + + sprintf(length, "%d", strlen(html)); + strcpy(response, header); + strcat(response, length); + strcat(response, "\r\n\r\n"); + strcat(response, html); + send(readSet.fd_array[i], response, strlen(response) + 1, 0); + goto finish; + } + // 在读取管道内容前,关闭写管道 + if (NULL != hWrite) + { + CloseHandle(hWrite); + hWrite = NULL; + } + + // 发送http头 + strcpy(response, "HTTP/1.1 200 OK\r\n"); + strcpy(header, "Server: plusplus123/0.1\r\n"); + strcat(response, header); + send(readSet.fd_array[i], response, strlen(response), 0); + printf("header=%s\n", header); + printf("response=%s\n", response); + + // 读取管道内的所有内容 + while (ReadFile(hRead, szBuffer, 1024, &dwRead, NULL)) + { + // printf("%s", szBuffer); + send(readSet.fd_array[i], szBuffer, strlen(szBuffer) + 1, 0); + memset(szBuffer, 0, 1024); + } + + // 等待一毫秒,等待浏览器接收完数据才关闭链接 + Sleep(100); + + CloseHandle(hRead); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + /***************************调用cgi end**************************/ + } + // 静态请求 + else + { + FILE *fp = NULL; + fp = fopen(temp_path, "r"); + + if (fp == NULL) + { + strcpy(header, "HTTP/1.1 404 Not Found\r\nServer: plusplus123/0.1\r\nContent-Type: text/html;charset=UTF-8\r\nContent-Length: "); + strcpy(html, "404

404 Not Found

+#include +#include + +#include "linklist.h" + +// 创建一个链表 +ListHead* link_create() +{ + ListHead *head; // 定义头节点 + head = (ListHead*)malloc(sizeof(ListHead)); // 分配地址 + head->first = NULL; + head->size = 0; + return head; +} +// 获取链表长度 +int link_get_size(ListHead* link_head) +{ + return link_head->size; +} +// 通过name获取value +ListStruct* link_get(ListHead* link_head, char *name) +{ + ListStruct *link_node; + for (link_node = link_head->first; link_node != NULL; link_node = link_node->next) + { + if (strcmp(link_node->name, name) == 0) + { + return link_node; + } + } + return NULL; +} +// 通过name设置value的值 +int link_set(ListHead* link_head, char *name, char *value) +{ + ListStruct *link_node = link_get(link_head, name); + if (link_node == NULL) + { + return 0; + } + strcpy(link_node->value, value); + return 1; +} +// 新增一个节点 +void link_add(ListHead* link_head, char *name, char *value) +{ + ListStruct *node = (ListStruct*)malloc(sizeof(ListStruct)); + strcpy(node->name, name); + strcpy(node->value, value); + if (link_head->first == NULL) + { + node->next = NULL; + } + else + { + node->next = link_head->first; + } + link_head->first = node; + link_head->size++; +} +// 删除一个节点 +int link_del(ListHead* link_head, char *name) +{ + // 没有节点的情况 + if (link_head->size == 0) + { + return 0; + } + + ListStruct *pre = NULL; + ListStruct *tag; + for (tag = link_head->first; tag != NULL; pre = tag, tag = tag->next) + { + if (strcmp(tag->name, name) == 0) + { + if (pre == NULL) + { + link_head->first = tag->next; + } + else + { + pre->next = tag->next; + } + free(tag); + link_head->size--; + return 1; + } + } + return 0; +} +// 输出链表 +void link_print(ListHead* link_head) +{ + if (link_head == NULL) + { + printf("empty\n"); + return; + } + ListStruct *node = link_head->first; + printf("len=%d\n", link_head->size); + while (node != NULL) + { + printf("node=%x\tname=%s\tvalue=%s\tnext=%x\n", node, node->name, node->value, node->next); + node = node->next; + } +} +// 输出单个节点 +void link_print_node(ListStruct *node) +{ + printf("node=%x\tname=%s\tvalue=%s\tnext=%x\n", node, node->name, node->value, node->next); +} +// 清空链表 +void link_free(ListHead* link_head) +{ + ListStruct *node, *next; + node = link_head->first; + if (node != NULL) + { + do { + next = node->next; + free(node); + node = next; + } while (next != NULL); + } + free(link_head); +} +// 通过index获取value,index从0开始 +ListStruct* link_get_index(ListHead* link_head, int index) +{ + int link_len = link_get_size(link_head) - 1; + ListStruct *tag; + int real_index = link_len - index; + int count = 0; + tag = link_head->first; + do + { + if (count == real_index) + { + return tag; + } + tag = tag->next; + count++; + } while (count < link_len); + + return NULL; +} \ No newline at end of file diff --git a/src/linklist.h b/src/linklist.h new file mode 100644 index 0000000..7ad573d --- /dev/null +++ b/src/linklist.h @@ -0,0 +1,37 @@ +#ifndef __LINKLIST_H__ +#define __LINKLIST_H__ + +// 链表节点 +typedef struct ListStruct +{ + char name[256]; + char value[2048]; + struct ListStruct *next; +} ListStruct; +// 链表表头 +typedef struct ListHead +{ + ListStruct *first; + int size; +} ListHead; +// 创建一个链表 +ListHead* link_create(); +// 获取链表长度 +int link_get_size(ListHead* link_head); +// 通过name获取value +ListStruct* link_get(ListHead* link_head, char *name); +// 通过name设置value的值 +int link_set(ListHead* link_head, char *name, char *value); +// 新增一个节点 +void link_add(ListHead* link_head, char *name, char *value); +// 删除一个节点 +int link_del(ListHead* link_head, char *name); +// 输出链表 +void link_print(ListHead* link_head); +// 输出单个节点 +void link_print_node(ListStruct *node); +// 清空链表 +void link_free(ListHead* link_head); +// 通过index获取value,index从0开始 +ListStruct* link_get_index(ListHead* link_head, int index); +#endif \ No newline at end of file diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 0000000..cdad418 --- /dev/null +++ b/src/parser.c @@ -0,0 +1,477 @@ +#include +#include +#include + +#include "parser.h" + +Request* requset_init() +{ + Request *request; // 定义头节点 + request = (Request*)malloc(sizeof(Request)); // 分配地址 + memset(request->method, 0, sizeof(char)*10); + memset(request->path, 0, sizeof(char)*236); + memset(request->http_version, 0, sizeof(char)*16); + memset(request->header_raw, 0, sizeof(char)*512); + memset(request->body_raw, 0, sizeof(char)*512); + memset(request->query_string, 0, sizeof(char)*236); + memset(request->request_uri, 0, sizeof(char)*256); + request->header = link_create(); + request->post = link_create(); + request->get = link_create(); + request->cookie = link_create(); + return request; +} +void request_print(Request *request) +{ + if (request == NULL) + { + printf("empty\n"); + return; + } + printf("%x\n", request); + printf("method:\n"); + printf("%s\n", request->method); + printf("path:\n"); + printf("%s\n", request->path); + printf("http_version:\n"); + printf("%s\n", request->http_version); + printf("header_raw:\n"); + printf("%s\n", request->header_raw); + printf("body_raw:\n"); + printf("%s\n", request->body_raw); + printf("query_string:\n"); + printf("%s\n", request->query_string); + printf("request_uri:\n"); + printf("%s\n", request->request_uri); + printf("port:\n"); + printf("%s\n", request->port); + printf("ip:\n"); + printf("%s\n", request->ip); + printf("header:\n"); + link_print(request->header); + printf("post:\n"); + link_print(request->post); + printf("get:\n"); + link_print(request->get); + printf("cookie:\n"); + link_print(request->cookie); +} +void request_free(Request *request) +{ + link_free(request->header); + link_free(request->post); + link_free(request->get); + free(request); +} +// 复制字符串里的某一段到另一个字符串里 +int copy_cahr (char *src, char *dst, int start, int end) +{ + int i = start, k = 0; + for (; i < end; k++, i++) + { + dst[k] = src[i]; + } + dst[k] = 0; +} +// 删除字符串里的某个字符 +void del_chr(char *s, char ch) +{ + char *t = s; // 目标指针先指向原串头 + while(*s != '\0') // 遍历字符串s + { + if (*s != ch) // 如果当前字符不是要删除的,则保存到目标串中 + *t++ = *s; + s++; // 检查下一个字符 + } + *t = '\0'; // 置目标串结束符。 +} +// 替换单个字符 +void char_replace(char search, char replace, char *subject) +{ + int i = 0; + int len = strlen(subject); + do { + if (subject[i] == search) + { + subject[i] = replace; + } + i++; + } while (i < len); +} +void set_param(char *item, ListHead* request_body, char *separator) +{ + char name[256]; + char value[256]; + int i = 0; + int len = strlen(item); + int start = i; + int end; + int separator_len = strlen(separator); + char temp[64]; + do { + copy_cahr(item, temp, i, i+separator_len); + temp[separator_len] = '\0'; + if (strcmp(temp, separator) == 0) + { + end = i; + copy_cahr(item, name, start, end); + start = end + separator_len; + end = len; + copy_cahr(item, value, start, end); + link_add(request_body, name, value); + break; + } + i++; + } while (i < len); +} +void parser_kv(char *src, ListHead* kv, char *item_separator, char *value_separator) +{ + int i; + int len; + int start; + int end; + int item_separator_len; + char temp[64]; + char item[512]; + + i = 0; + start = i; + len = strlen(src); + item_separator_len = strlen(item_separator); + do { + copy_cahr(src, temp, i, i+item_separator_len); + temp[item_separator_len] = '\0'; + if (strcmp(temp, item_separator) == 0) { + end = i; + copy_cahr(src, item, start, end); + start = end + item_separator_len; + set_param(item, kv, value_separator); + } + i++; + } while (i < len); +} +void separate_request(char *request_raw, Request *request) +{ + char *method = request->method; + char *request_uri = request->request_uri; + char *http_version = request->http_version; + char *path = request->path; + char *query_string = request->query_string; + char status_line[512]; + char file_name[64]; + char file_full_name[64]; + char file_ext_name[32]; + int start; + int end; + int i; + int len; + ListStruct *node; + + i = 0; + len = strlen(request_raw); + do { + // 分离请求头和请求行 + if (request_raw[i] == '\r') + { + copy_cahr(request_raw, status_line, 0, i); + i = i + 2; + start = i; + // 分离请求头和请求体 + do { + if (request_raw[i] == '\r' && request_raw[i+1] == '\n' && request_raw[i+2] == '\r' && request_raw[i+3] == '\n') + { + end = i + 2; // 把最后一个\r\n也包含进去 + copy_cahr(request_raw, request->header_raw, start, end); + break; + } + i++; + } while (i < len); + break; + } + i++; + } while (i < len); + + parser_kv(request->header_raw, request->header, "\r\n", ": "); + + // 分离ip,端口 + node = link_get(request->header, "Host"); + if (node != NULL) + { + i = 0; + start = i; + len = strlen(node->value); + do { + if (node->value[i] == ':') + { + end = i; + copy_cahr(node->value, request->ip, start, end); + start = i + 1; + end = len; + copy_cahr(node->value, request->port, start, end); + break; + } + i++; + } while(i < len); + } + node = NULL; + + // 分离请求行 + len = strlen(status_line); + i = 0; + start = i; + end; + do { + // 分离请求方法 + if (status_line[i] == ' ' && method[0] == 0) + { + end = i; + copy_cahr(status_line, method, start, end); + start = end + 1; + } + // 分离url + else if (status_line[i] == ' ' && request_uri[0] == 0) + { + end = i; + copy_cahr(status_line, request_uri, start, end); + start = end + 1; + } + // 分离http版本 + else if (status_line[i+1] == '\0' && http_version[0] == 0) + { + end = len; + copy_cahr(status_line, http_version, start, end); + break; + } + i++; + } while (i < len); + + // 分离query_string参数 + len = strlen(request_uri); + i = 0; + start = i; + do { + if (request_uri[i] == '?') + { + end = i; + copy_cahr(request_uri, path, start, end); + start = end + 1; + end = len; + copy_cahr(request_uri, query_string, start, end); + break; + } + i++; + } while (i <= len); + if (path[0] == 0) + { + copy_cahr(request_uri, path, 0, len); + } + + // 分离cookie + node = link_get(request->header, "Cookie"); + if (node != NULL) + { + len = strlen(node->value); + node->value[len] = ';'; + node->value[len+1] = ' '; + node->value[len+2] = '\0'; + parser_kv(node->value, request->cookie, "; ", "="); + } +} +void get_request_param(char *request_body_raw, ListHead* request_body) +{ + int i = 0; + int len = strlen(request_body_raw); + int start = i; + int end; + char item[64]; + do { + if (request_body_raw[i] == '&' || request_body_raw[i] == '\0') + { + end = i; + copy_cahr(request_body_raw, item, start, end); + start = end + 1; + set_param(item, request_body, "="); + } + i++; + } while (i <= len); +} +// 获取请求行 +void get_header(char *request_raw, Request *request) +{ + char *method = request->method; + char *request_uri = request->request_uri; + char *path = request->path; + char *query_string = request->query_string; + char status_line[512]; + char header_raw[1024]; + int start; + int end; + int i = 0; + int len = strlen(request_raw); + do { + if (request_raw[i] == '\r') + { + copy_cahr(request_raw, status_line, 0, i); + i = i + 2; + start = i; + do { + if (request_raw[i] == '\r' && request_raw[i+1] == '\n' && request_raw[i+2] == '\r' && request_raw[i+3] == '\n') + { + end = i; + copy_cahr(request_raw, header_raw, start, end); + break; + } + i++; + } while(i < len); + break; + } + i++; + } while(i < len); + strcpy(request->header_raw, header_raw); + i = 0; + len = strlen(header_raw); + start = i; + end; + char item[64]; + do { + if ((header_raw[i] == '\r' && header_raw[i+1] == '\n') || header_raw[i] == '\0') + { + end = i; + copy_cahr(header_raw, item, start, end); + start = end + 2; + set_param(item, request->header, ": "); + } + i++; + } while (i <= len); + + ListStruct *node = link_get(request->header, "Host"); + if (node != NULL) + { + i = 0; + start = i; + len = strlen(node->value); + do { + if (node->value[i] == ':') + { + end = i; + copy_cahr(node->value, request->ip, start, end); + start = i + 1; + end = len; + copy_cahr(node->value, request->port, start, end); + break; + } + i++; + } while(i < len); + } + + len = strlen(status_line); + i = 0; + start = i; + end; + do { + if (status_line[i] == 32 && method[0] == 0) + { + end = i; + copy_cahr(status_line, method, start, end); + start = end + 1; + } + else if (status_line[i] == 32 && request_uri[0] == 0) + { + end = i; + copy_cahr(status_line, request_uri, start, end); + start = end + 1; + } + i++; + } while (i < len); + + len = strlen(request_uri); + i = 0; + start = i; + do { + if (request_uri[i] == '?') + { + end = i; + copy_cahr(request_uri, path, start, end); + start = end + 1; + end = len; + copy_cahr(request_uri, query_string, start, end); + break; + } + i++; + } while (i <= len); +} +void get_request_body_raw(char *request_raw, char *request_body) +{ + int i = 0; + int len = strlen(request_raw); + do { + if (request_raw[i] == '\r' && request_raw[i+1] == '\n' && request_raw[i+2] == '\r' && request_raw[i+3] == '\n') + { + i = i + 4; + break; + } + i++; + } while (i < len); + copy_cahr(request_raw, request_body, i, len); +} +void parser_request(Request *request, char *request_raw) +{ + // get_header(request_raw, request); + separate_request(request_raw, request); + get_request_param(request->query_string, request->get); + get_request_body_raw(request_raw, request->body_raw); + get_request_param(request->body_raw, request->post); +} +char* is_cgi_script(char *path, char *cgi_script[], int script_count) +{ + int i; + int j; + int path_len; + int script_len; + char temp[64]; + + path_len = strlen(path); + for (i = 0; i < script_count; i++) + { + script_len = strlen(cgi_script[i]); + copy_cahr(path, temp, path_len - script_len, path_len); + + if (strcmp(temp, cgi_script[i]) == 0) { + return cgi_script[i]; + } + } + return NULL; +} +void get_script_interpreter(char *path, char *interpreter) +{ + FILE *fp = NULL; + fp = fopen(path, "r"); + if (fp == NULL) + { + return; + } + + char ch; + int i2 = 0; + char first_line[256]; + memset(first_line, 0, sizeof(char)*256); + while(!((ch = fgetc(fp)) == '\n' || ch == '\r')) + { + first_line[i2] = ch; + i2++; + } + + if (first_line[0] != '#' || first_line[1] != '!') + { + interpreter[0] = '\0'; + return; + } + + copy_cahr(first_line, interpreter, 2, strlen(first_line)); + char temp[256]; + memset(temp, 0, sizeof(char)*256); + strcat(temp, "\""); + strcat(temp, interpreter); + strcat(temp, "\""); + copy_cahr(temp, interpreter, 0, strlen(temp)); +} \ No newline at end of file diff --git a/src/parser.h b/src/parser.h new file mode 100644 index 0000000..6312a4f --- /dev/null +++ b/src/parser.h @@ -0,0 +1,41 @@ +#ifndef __PARSER_H__ +#define __PARSER_H__ + +#include "linklist.h" + +// 保存http请求内容的结构体 +typedef struct Request +{ + char method[10]; + char path[236]; + char header_raw[1024]; + char body_raw[1024]; + char query_string[236]; + char request_uri[256]; + char port[6]; + char ip[16]; + char http_version[16]; + ListHead *header; + ListHead *post; + ListHead *get; + ListHead *cookie; +} Request; + +// 复制字符串里的某一段到另一个字符串里 +int copy_cahr (char *src, char *dst, int start, int end); +// 删除字符串里的某个字符 +void del_chr(char *s, char ch); + +Request* requset_init(); +void request_print(Request *request); +void request_free(Request *request); +void set_param(char *item, ListHead* request_body, char *separator); +void get_request_param(char *request_body_raw, ListHead* request_body); +void get_header(char *request_raw, Request *request); +void get_request_body_raw(char *request_raw, char *request_body); +void parser_request(Request *request, char *request_raw); +void parser_kv(char *src, ListHead* kv, char *item_separator, char *value_separator); +void char_replace(char search, char replace, char *subject); +char* is_cgi_script(char *paht, char *cgi_script[], int script_count); +void get_script_interpreter(char *path, char *first_line); +#endif \ No newline at end of file diff --git a/src/test.c b/src/test.c new file mode 100644 index 0000000..fd73c5e --- /dev/null +++ b/src/test.c @@ -0,0 +1,23 @@ +#include +#include + +int main(int argc, char *argv[], char **envp) +{ + printf("Content-Type: text/html; charset=utf-8\n\n"); + printf("
\n");
+    printf("test");
+    printf( "\nEnvironment variables:\n" );
+    while( *envp != NULL )
+        printf( "  %s\n", *(envp++) );
+
+    printf("PATH=%s\n", getenv("PATH"));
+    printf("THIS=%s\n", getenv("THIS"));
+
+    printf("\n%s\n", envp);
+
+    char poststr[4096];
+    fgets(poststr, 4096, stdin);
+    printf("\npoststr=%s\n", poststr);
+    printf("
\n"); + return 0; +} \ No newline at end of file diff --git a/target/index.html b/target/index.html new file mode 100644 index 0000000..a748d4b --- /dev/null +++ b/target/index.html @@ -0,0 +1,14 @@ + + + + + + + Template + + + +

hello world6

+ + + \ No newline at end of file diff --git a/target/test.cgi b/target/test.cgi new file mode 100644 index 0000000..2efe38d Binary files /dev/null and b/target/test.cgi differ diff --git a/target/test.php b/target/test.php new file mode 100644 index 0000000..5415bd1 --- /dev/null +++ b/target/test.php @@ -0,0 +1,6 @@ +#!php +") +print("环境变量
") +print("
    ") +for key in os.environ.keys(): + print("
  • %30s : %s
  • " % (key,os.environ[key])) +output = os.popen('chcp') +chcp = "
  • "+output.read()+"
  • " +print(chcp) +print("
") +print("hello world") \ No newline at end of file