📄 response.c
字号:
/*! \file response.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: http response handling $Id: response.c,v 1.3 2005/06/03 04:43:33 s_zander Exp $*/#include "config.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <fcntl.h>#include <errno.h>#include <syslog.h>#include <time.h>#include <sys/time.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <netinet/tcp.h>#include "httpd.h"/* ---------------------------------------------------------------------- *//* os-specific sendfile() wrapper *//* * int xsendfile(out,in,offset,bytes) * * out - outgoing filedescriptor (i.e. the socket) * in - incoming filedescriptor (i.e. the file to send out) * offset - file offset (where to start) * bytes - number of bytes to send * * return value * on error: -1 and errno set. * on success: the number of successfully written bytes (which might * be smaller than bytes, we are doing nonblocking I/O). * extra hint: much like write(2) works. * */#if defined(__linux__) && !defined(NO_SENDFILE)# include <sys/sendfile.h>static int xsendfile(int out, int in, off_t offset, size_t bytes){ return sendfile(out, in, &offset, bytes);}#elif defined(__FreeBSD__) && !defined(NO_SENDFILE)static int xsendfile(int out, int in, off_t offset, size_t bytes){ off_t nbytes = 0; if (sendfile(in, out, offset, bytes, NULL, &nbytes, 0) == -1) { /* Why the heck FreeBSD returns an /error/ if it has done a partial write? With non-blocking I/O this absolutely normal behavoir and no error at all. Stupid. */ if (errno == EAGAIN && nbytes > 0) { return nbytes; } return -1; } return nbytes;}#else# warning using slow sendfile() emulation./* Poor man's sendfile() implementation. Performance sucks, but it works. */#define BUFSIZE 16384static int xsendfile(int out, int in, off_t offset, size_t bytes){ char buf[BUFSIZE]; ssize_t nread; ssize_t nsent, nsent_total; if (lseek(in, offset, SEEK_SET) == -1) {#ifdef DEBUG perror("lseek");#endif return -1; } nsent = nsent_total = 0; for (;bytes > 0;) { /* read a block */ nread = read(in, buf, (bytes < BUFSIZE) ? bytes : BUFSIZE); if (nread == -1) {#ifdef DEBUG perror("read");#endif return nsent_total ? nsent_total : -1; } if (nread == 0) { break; } /* write it out */ nsent = write(out, buf, nread); if (nsent == -1) { return nsent_total ? nsent_total : -1; } nsent_total += nsent; if (nsent < nread) { /* that was a partial write only. Queue full. Bailout here, the next write would return EAGAIN anyway... */ break; } bytes -= nread; } return nsent_total;}#endif/* ---------------------------------------------------------------------- */#ifdef USE_SSLstatic inline int wrap_xsendfile(struct REQUEST *req, off_t off, size_t bytes){ if (with_ssl) { return ssl_blk_write(req,off,bytes); } else { return xsendfile(req->fd, req->bfd, off, bytes); }}static inline int wrap_write(struct REQUEST *req, void *buf, size_t bytes){ if (with_ssl) { return ssl_write(req,buf,bytes); } else { return write(req->fd, buf, bytes); }}#elsestatic inline int wrap_xsendfile(struct REQUEST *req, off_t off, size_t bytes){ return xsendfile(req->fd,req->bfd,off,bytes);}static inline int wrap_write(struct REQUEST *req, void *buf, size_t bytes){ return write(req->fd,buf,bytes);}#endif/* ---------------------------------------------------------------------- */static struct HTTP_STATUS { int status; char *head; char *body;} http[] = { { 200, "200 OK", NULL }, { 206, "206 Partial Content", NULL }, { 304, "304 Not Modified", NULL }, { 400, "400 Bad Request", "*PLONK*\n" }, { 401, "401 Authentication required", "Authentication required\n" }, { 403, "403 Forbidden", "Access denied\n" }, { 404, "404 Not Found", "File or directory not found\n" }, { 408, "408 Request Timeout", "Request Timeout\n" }, { 412, "412 Precondition failed.", "Precondition failed\n" }, { 500, "500 Internal Server Error", "Sorry folks\n" }, { 501, "501 Not Implemented", "Sorry folks\n" }, { 0, NULL, NULL }};/* ---------------------------------------------------------------------- */#define RESPONSE_START \ "HTTP/1.1 %s\r\n" \ "Server: %s\r\n" \ "Connection: %s\r\n" \ "Accept-Ranges: bytes\r\n"#define RFCTIME \ "%a, %d %b %Y %H:%M:%S GMT"#define BOUNDARY \ "XXX_CUT_HERE_%ld_XXX"void mkerror(struct REQUEST *req, int status, int ka){ int i; for (i = 0; http[i].status != 0; i++) { if (http[i].status == status) { break; } } req->status = status; req->body = http[i].body; req->lbody = strlen(req->body); if (!ka) { req->keep_alive = 0; } req->lres = sprintf(req->hres, RESPONSE_START "Content-Type: text/plain\r\n" "Content-Length: %d\r\n", http[i].head,server_name, req->keep_alive ? "Keep-Alive" : "Close", req->lbody); if (401 == status) { req->lres += sprintf(req->hres+req->lres, "WWW-Authenticate: Basic realm=\"NetMate\"\r\n"); } req->lres += strftime(req->hres+req->lres,80, "Date: " RFCTIME "\r\n\r\n", gmtime(&now)); req->state = STATE_WRITE_HEADER;#ifdef DEBUG fprintf(stderr,"%03d/%d: error: %d, connection=%s\n", req->fd, req->state, status, req->keep_alive ? "Keep-Alive" : "Close");#endif}void mkredirect(struct REQUEST *req, int tcp_port){ req->status = 302; req->body = req->path; req->lbody = strlen(req->body); req->lres = sprintf(req->hres, RESPONSE_START "Location: http://%s:%d%s\r\n" "Content-Type: text/plain\r\n" "Content-Length: %d\r\n", "302 Redirect",server_name, req->keep_alive ? "Keep-Alive" : "Close", req->hostname,tcp_port,quote(req->path,9999), req->lbody); req->lres += strftime(req->hres+req->lres,80, "Date: " RFCTIME "\r\n\r\n", gmtime(&now)); req->state = STATE_WRITE_HEADER;#ifdef DEBUG fprintf(stderr,"%03d/%d: 302 redirect: %s, connection=%s\n", req->fd, req->state, req->path, req->keep_alive ? "Keep-Alive" : "Close");#endif}static int mkmulti(struct REQUEST *req, int i){ req->r_hlen[i] = sprintf(req->r_head+i*BR_HEADER, "\r\n--" BOUNDARY "\r\n" "Content-type: %s\r\n"#if (SIZEOF_OFF_T == 4) "Content-range: bytes %ld-%ld/%ld\r\n"#else "Content-range: bytes %lld-%lld/%lld\r\n"#endif "\r\n", (unsigned long)now, req->mime, req->r_start[i],req->r_end[i]-1,req->bst.st_size);#ifdef DEBUG#if (SIZEOF_OFF_T == 4)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -