一个几百行代码实现的http服务器tinyhttpd

  1 /* J. David's webserver */
  2 /* This is a simple webserver.
  3  * Created November 1999 by J. David Blackstone.
  4  * CSE 4344 (Network concepts), Prof. Zeigler
  5  * University of Texas at Arlington
  6  */
  7 /* This program compiles for Sparc Solaris 2.6.
  8  * To compile for Linux:
  9  *  1) Comment out the #include <pthread.h> 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 <stdio.h>
 16 #include <sys/socket.h>
 17 #include <sys/types.h>
 18 #include <netinet/in.h>
 19 #include <arpa/inet.h>
 20 #include <unistd.h>
 21 #include <ctype.h>
 22 #include <strings.h>
 23 #include <string.h>
 24 #include <sys/stat.h>
 25 #include <pthread.h>
 26 #include <sys/wait.h>
 27 #include <stdlib.h>
 28 #include <stdint.h>
 29 
 30 #define ISspace(x) isspace((int)(x))
 31 
 32 #define SERVER_STRING "Server: jdbhttpd/0.1.0rn"
 33 #define STDIN   0
 34 #define STDOUT  1
 35 #define STDERR  2
 36 
 37 void accept_request(void *);
 38 void bad_request(int);
 39 void cat(int, FILE *);
 40 void cannot_execute(int);
 41 void error_die(const char *);
 42 void execute_cgi(int, const char *, const char *, const char *);
 43 int get_line(int, char *, int);
 44 void headers(int, const char *);
 45 void not_found(int);
 46 void serve_file(int, const char *);
 47 int startup(u_short *);
 48 void unimplemented(int);
 49 
 50 /**********************************************************************/
 51 /* A request has caused a call to accept() on the server port to
 52  * return.  Process the request appropriately.
 53  * Parameters: the socket connected to the client */
 54 /**********************************************************************/
 55 void accept_request(void *arg)
 56 {
 57     int client = (intptr_t)arg;
 58     char buf[1024];
 59     size_t numchars;
 60     char method[255];
 61     char url[255];
 62     char path[512];
 63     size_t i, j;
 64     struct stat st;
 65     int cgi = 0;      /* becomes true if server decides this is a CGI
 66                        * program */
 67     char *query_string = NULL;
 68 
 69     numchars = get_line(client, buf, sizeof(buf));
 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] = '';
 78 
 79     if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
 80     {
 81         unimplemented(client);
 82         return;
 83     }
 84 
 85     if (strcasecmp(method, "POST") == 0)
 86         cgi = 1;
 87 
 88     i = 0;
 89     while (ISspace(buf[j]) && (j < numchars))
 90         j++;
 91     while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < numchars))
 92     {
 93         url[i] = buf[j];
 94         i++; j++;
 95     }
 96     url[i] = '';
 97 
 98     if (strcasecmp(method, "GET") == 0)
 99     {
100         query_string = url;
101         while ((*query_string != '?') && (*query_string != ''))
102             query_string++;
103         if (*query_string == '?')
104         {
105             cgi = 1;
106             *query_string = '';
107             query_string++;
108         }
109     }
110 
111     sprintf(path, "htdocs%s", url);
112     if (path[strlen(path) - 1] == '/')
113         strcat(path, "index.html");
114     if (stat(path, &st) == -1) {
115         while ((numchars > 0) && strcmp("n", buf))  /* read & discard headers */
116             numchars = get_line(client, buf, sizeof(buf));
117         not_found(client);
118     }
119     else
120     {
121         if ((st.st_mode & S_IFMT) == S_IFDIR)
122             strcat(path, "/index.html");
123         if ((st.st_mode & S_IXUSR) ||
124                 (st.st_mode & S_IXGRP) ||
125                 (st.st_mode & S_IXOTH)    )
126             cgi = 1;
127         if (!cgi)
128             serve_file(client, path);
129         else
130             execute_cgi(client, path, method, query_string);
131     }
132 
133     close(client);
134 }
135 
136 /**********************************************************************/
137 /* Inform the client that a request it has made has a problem.
138  * Parameters: client socket */
139 /**********************************************************************/
140 void bad_request(int client)
141 {
142     char buf[1024];
143 
144     sprintf(buf, "HTTP/1.0 400 BAD REQUESTrn");
145     send(client, buf, sizeof(buf), 0);
146     sprintf(buf, "Content-type: text/htmlrn");
147     send(client, buf, sizeof(buf), 0);
148     sprintf(buf, "rn");
149     send(client, buf, sizeof(buf), 0);
150     sprintf(buf, "<P>Your browser sent a bad request, ");
151     send(client, buf, sizeof(buf), 0);
152     sprintf(buf, "such as a POST without a Content-Length.rn");
153     send(client, buf, sizeof(buf), 0);
154 }
155 
156 /**********************************************************************/
157 /* Put the entire contents of a file out on a socket.  This function
158  * is named after the UNIX "cat" command, because it might have been
159  * easier just to do something like pipe, fork, and exec("cat").
160  * Parameters: the client socket descriptor
161  *             FILE pointer for the file to cat */
162 /**********************************************************************/
163 void cat(int client, FILE *resource)
164 {
165     char buf[1024];
166 
167     fgets(buf, sizeof(buf), resource);
168     while (!feof(resource))
169     {
170         send(client, buf, strlen(buf), 0);
171         fgets(buf, sizeof(buf), resource);
172     }
173 }
174 
175 /**********************************************************************/
176 /* Inform the client that a CGI script could not be executed.
177  * Parameter: the client socket descriptor. */
178 /**********************************************************************/
179 void cannot_execute(int client)
180 {
181     char buf[1024];
182 
183     sprintf(buf, "HTTP/1.0 500 Internal Server Errorrn");
184     send(client, buf, strlen(buf), 0);
185     sprintf(buf, "Content-type: text/htmlrn");
186     send(client, buf, strlen(buf), 0);
187     sprintf(buf, "rn");
188     send(client, buf, strlen(buf), 0);
189     sprintf(buf, "<P>Error prohibited CGI execution.rn");
190     send(client, buf, strlen(buf), 0);
191 }
192 
193 /**********************************************************************/
194 /* Print out an error message with perror() (for system errors; based
195  * on value of errno, which indicates system call errors) and exit the
196  * program indicating an error. */
197 /**********************************************************************/
198 void error_die(const char *sc)
199 {
200     perror(sc);
201     exit(1);
202 }
203 
204 /**********************************************************************/
205 /* Execute a CGI script.  Will need to set environment variables as
206  * appropriate.
207  * Parameters: client socket descriptor
208  *             path to the CGI script */
209 /**********************************************************************/
210 void execute_cgi(int client, const char *path,
211         const char *method, const char *query_string)
212 {
213     char buf[1024];
214     int cgi_output[2];
215     int cgi_input[2];
216     pid_t pid;
217     int status;
218     int i;
219     char c;
220     int numchars = 1;
221     int content_length = -1;
222 
223     buf[0] = 'A'; buf[1] = '';
224     if (strcasecmp(method, "GET") == 0)
225         while ((numchars > 0) && strcmp("n", buf))  /* read & discard headers */
226             numchars = get_line(client, buf, sizeof(buf));
227     else if (strcasecmp(method, "POST") == 0) /*POST*/
228     {
229         numchars = get_line(client, buf, sizeof(buf));
230         while ((numchars > 0) && strcmp("n", buf))
231         {
232             buf[15] = '';
233             if (strcasecmp(buf, "Content-Length:") == 0)
234                 content_length = atoi(&(buf[16]));
235             numchars = get_line(client, buf, sizeof(buf));
236         }
237         if (content_length == -1) {
238             bad_request(client);
239             return;
240         }
241     }
242     else/*HEAD or other*/
243     {
244     }
245 
246 
247     if (pipe(cgi_output) < 0) {
248         cannot_execute(client);
249         return;
250     }
251     if (pipe(cgi_input) < 0) {
252         cannot_execute(client);
253         return;
254     }
255 
256     if ( (pid = fork()) < 0 ) {
257         cannot_execute(client);
258         return;
259     }
260     sprintf(buf, "HTTP/1.0 200 OKrn");
261     send(client, buf, strlen(buf), 0);
262     if (pid == 0)  /* child: CGI script */
263     {
264         char meth_env[255];
265         char query_env[255];
266         char length_env[255];
267 
268         dup2(cgi_output[1], STDOUT);
269         dup2(cgi_input[0], STDIN);
270         close(cgi_output[0]);
271         close(cgi_input[1]);
272         sprintf(meth_env, "REQUEST_METHOD=%s", method);
273         putenv(meth_env);
274         if (strcasecmp(method, "GET") == 0) {
275             sprintf(query_env, "QUERY_STRING=%s", query_string);
276             putenv(query_env);
277         }
278         else {   /* POST */
279             sprintf(length_env, "CONTENT_LENGTH=%d", content_length);
280             putenv(length_env);
281         }
282         execl(path, NULL);
283         exit(0);
284     } else {    /* parent */
285         close(cgi_output[1]);
286         close(cgi_input[0]);
287         if (strcasecmp(method, "POST") == 0)
288             for (i = 0; i < content_length; i++) {
289                 recv(client, &c, 1, 0);
290                 write(cgi_input[1], &c, 1);
291             }
292         while (read(cgi_output[0], &c, 1) > 0)
293             send(client, &c, 1, 0);
294 
295         close(cgi_output[0]);
296         close(cgi_input[1]);
297         waitpid(pid, &status, 0);
298     }
299 }
300 
301 /**********************************************************************/
302 /* Get a line from a socket, whether the line ends in a newline,
303  * carriage return, or a CRLF combination.  Terminates the string read
304  * with a null character.  If no newline indicator is found before the
305  * end of the buffer, the string is terminated with a null.  If any of
306  * the above three line terminators is read, the last character of the
307  * string will be a linefeed and the string will be terminated with a
308  * null character.
309  * Parameters: the socket descriptor
310  *             the buffer to save the data in
311  *             the size of the buffer
312  * Returns: the number of bytes stored (excluding null) */
313 /**********************************************************************/
314 int get_line(int sock, char *buf, int size)
315 {
316     int i = 0;
317     char c = '';
318     int n;
319 
320     while ((i < size - 1) && (c != 'n'))
321     {
322         n = recv(sock, &c, 1, 0);
323         /* DEBUG printf("%02Xn", c); */
324         if (n > 0)
325         {
326             if (c == 'r')
327             {
328                 n = recv(sock, &c, 1, MSG_PEEK);
329                 /* DEBUG printf("%02Xn", c); */
330                 if ((n > 0) && (c == 'n'))
331                     recv(sock, &c, 1, 0);
332                 else
333                     c = 'n';
334             }
335             buf[i] = c;
336             i++;
337         }
338         else
339             c = 'n';
340     }
341     buf[i] = '';
342 
343     return(i);
344 }
345 
346 /**********************************************************************/
347 /* Return the informational HTTP headers about a file. */
348 /* Parameters: the socket to print the headers on
349  *             the name of the file */
350 /**********************************************************************/
351 void headers(int client, const char *filename)
352 {
353     char buf[1024];
354     (void)filename;  /* could use filename to determine file type */
355 
356     strcpy(buf, "HTTP/1.0 200 OKrn");
357     send(client, buf, strlen(buf), 0);
358     strcpy(buf, SERVER_STRING);
359     send(client, buf, strlen(buf), 0);
360     sprintf(buf, "Content-Type: text/htmlrn");
361     send(client, buf, strlen(buf), 0);
362     strcpy(buf, "rn");
363     send(client, buf, strlen(buf), 0);
364 }
365 
366 /**********************************************************************/
367 /* Give a client a 404 not found status message. */
368 /**********************************************************************/
369 void not_found(int client)
370 {
371     char buf[1024];
372 
373     sprintf(buf, "HTTP/1.0 404 NOT FOUNDrn");
374     send(client, buf, strlen(buf), 0);
375     sprintf(buf, SERVER_STRING);
376     send(client, buf, strlen(buf), 0);
377     sprintf(buf, "Content-Type: text/htmlrn");
378     send(client, buf, strlen(buf), 0);
379     sprintf(buf, "rn");
380     send(client, buf, strlen(buf), 0);
381     sprintf(buf, "<HTML><TITLE>Not Found</TITLE>rn");
382     send(client, buf, strlen(buf), 0);
383     sprintf(buf, "<BODY><P>The server could not fulfillrn");
384     send(client, buf, strlen(buf), 0);
385     sprintf(buf, "your request because the resource specifiedrn");
386     send(client, buf, strlen(buf), 0);
387     sprintf(buf, "is unavailable or nonexistent.rn");
388     send(client, buf, strlen(buf), 0);
389     sprintf(buf, "</BODY></HTML>rn");
390     send(client, buf, strlen(buf), 0);
391 }
392 
393 /**********************************************************************/
394 /* Send a regular file to the client.  Use headers, and report
395  * errors to client if they occur.
396  * Parameters: a pointer to a file structure produced from the socket
397  *              file descriptor
398  *             the name of the file to serve */
399 /**********************************************************************/
400 void serve_file(int client, const char *filename)
401 {
402     FILE *resource = NULL;
403     int numchars = 1;
404     char buf[1024];
405 
406     buf[0] = 'A'; buf[1] = '';
407     while ((numchars > 0) && strcmp("n", buf))  /* read & discard headers */
408         numchars = get_line(client, buf, sizeof(buf));
409 
410     resource = fopen(filename, "r");
411     if (resource == NULL)
412         not_found(client);
413     else
414     {
415         headers(client, filename);
416         cat(client, resource);
417     }
418     fclose(resource);
419 }
420 
421 /**********************************************************************/
422 /* This function starts the process of listening for web connections
423  * on a specified port.  If the port is 0, then dynamically allocate a
424  * port and modify the original port variable to reflect the actual
425  * port.
426  * Parameters: pointer to variable containing the port to connect on
427  * Returns: the socket */
428 /**********************************************************************/
429 int startup(u_short *port)
430 {
431     int httpd = 0;
432     int on = 1;
433     struct sockaddr_in name;
434 
435     httpd = socket(PF_INET, SOCK_STREAM, 0);
436     if (httpd == -1)
437         error_die("socket");
438     memset(&name, 0, sizeof(name));
439     name.sin_family = AF_INET;
440     name.sin_port = htons(*port);
441     name.sin_addr.s_addr = htonl(INADDR_ANY);
442     if ((setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0)  
443     {  
444         error_die("setsockopt failed");
445     }
446     if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
447         error_die("bind");
448     if (*port == 0)  /* if dynamically allocating a port */
449     {
450         socklen_t namelen = sizeof(name);
451         if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
452             error_die("getsockname");
453         *port = ntohs(name.sin_port);
454     }
455     if (listen(httpd, 5) < 0)
456         error_die("listen");
457     return(httpd);
458 }
459 
460 /**********************************************************************/
461 /* Inform the client that the requested web method has not been
462  * implemented.
463  * Parameter: the client socket */
464 /**********************************************************************/
465 void unimplemented(int client)
466 {
467     char buf[1024];
468 
469     sprintf(buf, "HTTP/1.0 501 Method Not Implementedrn");
470     send(client, buf, strlen(buf), 0);
471     sprintf(buf, SERVER_STRING);
472     send(client, buf, strlen(buf), 0);
473     sprintf(buf, "Content-Type: text/htmlrn");
474     send(client, buf, strlen(buf), 0);
475     sprintf(buf, "rn");
476     send(client, buf, strlen(buf), 0);
477     sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implementedrn");
478     send(client, buf, strlen(buf), 0);
479     sprintf(buf, "</TITLE></HEAD>rn");
480     send(client, buf, strlen(buf), 0);
481     sprintf(buf, "<BODY><P>HTTP request method not supported.rn");
482     send(client, buf, strlen(buf), 0);
483     sprintf(buf, "</BODY></HTML>rn");
484     send(client, buf, strlen(buf), 0);
485 }
486 
487 /**********************************************************************/
488 
489 int main(void)
490 {
491     int server_sock = -1;
492     u_short port = 4000;
493     int client_sock = -1;
494     struct sockaddr_in client_name;
495     socklen_t  client_name_len = sizeof(client_name);
496     pthread_t newthread;
497 
498     server_sock = startup(&port);
499     printf("httpd running on port %dn", port);
500 
501     while (1)
502     {
503         client_sock = accept(server_sock,
504                 (struct sockaddr *)&client_name,
505                 &client_name_len);
506         if (client_sock == -1)
507             error_die("accept");
508         /* accept_request(&client_sock); */
509         if (pthread_create(&newthread , NULL, (void *)accept_request, (void *)(intptr_t)client_sock) != 0)
510             perror("pthread_create");
511     }
512 
513     close(server_sock);
514 
515     return(0);
516 }

其实就是建立tcp连接,通过对数据包的解析是否有http的头字段来判断是不是http的,wireshark就是这样

下面是一个别人总结的图

一个几百行代码实现的http服务器tinyhttpd

这个我自己写了一下但是没实现到cgi那里,贴一个别人用windows实现的地址

https://blog.csdn.net/magictong/article/details/53201038

原文链接: https://www.cnblogs.com/wangshaowei/p/9178532.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    一个几百行代码实现的http服务器tinyhttpd

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/391804

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月31日 上午10:46
下一篇 2023年3月31日 上午10:46

相关推荐