📄 httpd.c
字号:
/*! \file httpd.c Based on webfs Copyright (C) 1999-2001 Gerd Knorr This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License with the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Description: httpd main functions $Id: httpd.c,v 1.1.1.1 2004/12/23 05:52:34 s_zander Exp $*/#include "config.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <errno.h>#include <signal.h>#include <syslog.h>#include <fcntl.h>#include <time.h>#include <pwd.h>#include <grp.h>#include <sys/time.h>#include <sys/types.h>#include <sys/wait.h>#include <sys/signal.h>#include <sys/utsname.h>#include <sys/socket.h>#include <sys/un.h>#include <netinet/in.h>#include <netinet/tcp.h>#include <arpa/inet.h>#include <netdb.h>#include "httpd.h"/* public variables - server configuration *//* mime definitions */#define MIMEFILE "/etc/mime.types"/* configuration */static int timeout = 60; /* network timeout */static int keepalive_time = 5; /* keepalive time for connections */static char *mimetypes = MIMEFILE;static int max_conn = 32; /* max simulatneous connections *//* globals */static int tcp_port = 0; /* port number */static char server_host[256]; /* server name */static int slisten = -1;/* connection list */static struct REQUEST *conns = NULL;/* number of connections */static int curr_conn = 0;#ifdef HTTPD_USE_THREADSstatic int nthreads = 1;static pthread_t *threads;#endif/* public stuff */char *server_name = NULL; /* our name used in responses */time_t now = 0;#ifdef USE_SSLint with_ssl = 0;#endif/* callback functions */access_check_func_t access_check_func = NULL;parse_request_func_t parse_request_func = NULL;log_request_func_t log_request_func = NULL;log_error_func_t log_error_func = NULL;/* queue a OK response for request req */int httpd_send_response(struct REQUEST *req, fd_sets_t *fds){ time_t now; if (req->body != NULL) { now = time(NULL); req->lbody = strlen(req->body); /* 200 OK */ mkheader(req,200,now); } else { mkerror(req,500,1); } if (req->state == STATE_WRITE_HEADER) { // switch to writing FD_CLR(req->fd, &fds->rset); FD_SET(req->fd, &fds->wset); } return 0;} /* immediatly send back an OK response to request req can be used for short transactions which require no further processing of the app */int httpd_send_immediate_response(struct REQUEST *req){ time_t now; if (req->body != NULL) { now = time(NULL); req->lbody = strlen(req->body); /* 200 OK */ mkheader(req,200,now); } else { mkerror(req,500,1); } return 0;} /* handle a file descriptor event */int httpd_handle_event(fd_set *rset, fd_set *wset, fd_sets_t *fds){ struct REQUEST *req, *prev, *tmp; int length; int opt = 0; now = time(NULL); /* new connection ? */ if ((rset != NULL) && FD_ISSET(slisten, rset)) { req = malloc(sizeof(struct REQUEST)); if (NULL == req) { /* oom: let the request sit in the listen queue */#ifdef DEBUG fprintf(stderr,"oom\n");#endif } else { memset(req,0,sizeof(struct REQUEST)); if ((req->fd = accept(slisten,NULL,&opt)) == -1) { if (EAGAIN != errno) { log_error_func(1, LOG_WARNING,"accept",NULL); } free(req); } else { fcntl(req->fd,F_SETFL,O_NONBLOCK); req->bfd = -1; req->state = STATE_READ_HEADER; req->ping = now; req->lifespan = -1; req->next = conns; conns = req; curr_conn++;#ifdef DEBUG fprintf(stderr,"%03d/%d: new request (%d)\n",req->fd,req->state,curr_conn);#endif#ifdef USE_SSL if (with_ssl) { open_ssl_session(req); }#endif length = sizeof(req->peer); if (getpeername(req->fd,(struct sockaddr*)&(req->peer),&length) == -1) { log_error_func(1, LOG_WARNING,"getpeername",NULL); req->state = STATE_CLOSE; } getnameinfo((struct sockaddr*)&req->peer,length, req->peerhost,64,req->peerserv,8, NI_NUMERICHOST | NI_NUMERICSERV);#ifdef DEBUG fprintf(stderr,"%03d/%d: connect from (%s)\n", req->fd,req->state,req->peerhost);#endif /* host auth callback */ if (access_check_func != NULL) { if (access_check_func(req->peerhost, NULL) < 0) { /* read request */ read_header(req,0); req->ping = now; /* reply with access denied and close connection */ mkerror(req,403,0); write_request(req); req->state = STATE_CLOSE; } } FD_SET(req->fd, &fds->rset); if (req->fd > fds->max) { fds->max = req->fd; } } } } /* check active connections */ for (req = conns, prev = NULL; req != NULL;) { /* I/O */ if ((rset != NULL) && FD_ISSET(req->fd, rset)) { if (req->state == STATE_KEEPALIVE) { req->state = STATE_READ_HEADER; } if (req->state == STATE_READ_HEADER) { while (read_header(req,0) > 0); } if (req->state == STATE_READ_BODY) { while (read_body(req, 0) >0); } req->ping = now; } if ((wset != NULL) && FD_ISSET(req->fd, wset)) { write_request(req); req->ping = now; } /* check timeouts */ if (req->state == STATE_KEEPALIVE) { if (now > req->ping + keepalive_time || curr_conn > max_conn * 9 / 10) {#ifdef DEBUG fprintf(stderr,"%03d/%d: keepalive timeout\n",req->fd,req->state);#endif req->state = STATE_CLOSE; } } else { if (now > req->ping + timeout) { if ((req->state == STATE_READ_HEADER) || (req->state == STATE_READ_BODY)) { mkerror(req,408,0); } else { log_error_func(0,LOG_INFO,"network timeout",req->peerhost); req->state = STATE_CLOSE; } } } /* parsing */ parsing: if (req->state == STATE_PARSE_HEADER) { parse_request(req, server_host); } /* body parsing */ if (req->state == STATE_PARSE_BODY) { parse_request_body(req); } if (req->state == STATE_WRITE_HEADER) { /* switch to writing */ FD_CLR(req->fd, &fds->rset); FD_SET(req->fd, &fds->wset); write_request(req); } /* handle finished requests */ if (req->state == STATE_FINISHED && !req->keep_alive) { req->state = STATE_CLOSE; } if (req->state == STATE_FINISHED) { /* access log hook */ if (log_request_func != NULL) { log_request_func(req, now); } /* switch to reading */ FD_CLR(req->fd, &fds->wset); FD_SET(req->fd, &fds->rset); /* cleanup */ req->auth[0] = 0; req->if_modified = 0; req->if_unmodified = 0; req->if_range = 0; req->range_hdr = NULL; req->ranges = 0; if (req->r_start) { free(req->r_start); req->r_start = NULL; } if (req->r_end) { free(req->r_end); req->r_end = NULL; } if (req->r_head) { free(req->r_head); req->r_head = NULL; } if (req->r_hlen) { free(req->r_hlen); req->r_hlen = NULL; } list_free(&req->header); if (req->bfd != -1) { close(req->bfd); req->bfd = -1; } /* free memory of response body */ if ((req->status<400) && (req->body != NULL)) { free(req->body); req->body = NULL; } req->written = 0; req->head_only = 0; req->rh = 0; req->rb = 0; req->hostname[0] = 0; req->path[0] = 0; req->query[0] = 0; req->lifespan = -1; if (req->hdata == (req->lreq + req->lbreq)) { /* ok, wait for the next one ... */#ifdef DEBUG fprintf(stderr,"%03d/%d: keepalive wait\n",req->fd,req->state);#endif req->state = STATE_KEEPALIVE; req->hdata = 0; req->lreq = 0; req->lbreq = 0;#ifdef TCP_CORK if (req->tcp_cork == 1) { req->tcp_cork = 0;#ifdef DEBUG
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -