/boa-0.94.14rc21/src/pipe.c
C | 366 lines | 243 code | 48 blank | 75 comment | 111 complexity | faf538ec3962683ef66a0497030e2a56 MD5 | raw file
Possible License(s): GPL-2.0
1
2/*
3 * Boa, an http server
4 * Based on code Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
5 * Copyright (C) 1997-2004 Jon Nelson <jnelson@boa.org>
6 * Copyright (C) 1997-2005 Larry Doolittle <ldoolitt@boa.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 1, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 */
23
24/* $Id: pipe.c,v 1.39.2.16 2005/02/22 14:13:03 jnelson Exp $*/
25
26#include "boa.h"
27
28/*
29 * Name: read_from_pipe
30 * Description: Reads data from a pipe
31 *
32 * Return values:
33 * -1: request blocked, move to blocked queue
34 * 0: EOF or error, close it down
35 * 1: successful read, recycle in ready queue
36 */
37
38int read_from_pipe(request * req)
39{
40 int bytes_read; /* signed */
41 unsigned int bytes_to_read; /* unsigned */
42
43 bytes_to_read = BUFFER_SIZE - (req->header_end - req->buffer - 1);
44
45 if (bytes_to_read == 0) { /* buffer full */
46 if (req->cgi_status == CGI_PARSE) { /* got+parsed header */
47 req->cgi_status = CGI_BUFFER;
48 *req->header_end = '\0'; /* points to end of read data */
49 /* Could the above statement overwrite data???
50 No, because req->header_end points to where new data
51 should begin, not where old data is.
52 */
53 return process_cgi_header(req); /* cgi_status will change */
54 }
55 req->status = PIPE_WRITE;
56 return 1;
57 }
58
59 bytes_read = read(req->data_fd, req->header_end, bytes_to_read);
60#ifdef FASCIST_LOGGING
61 if (bytes_read > 0) {
62 *(req->header_end + bytes_read) = '\0';
63 fprintf(stderr, "pipe.c - read %d bytes: \"%s\"\n",
64 bytes_read, req->header_end);
65 } else
66 fprintf(stderr, "pipe.c - read %d bytes\n", bytes_read);
67 fprintf(stderr, "status, cgi_status: %d, %d\n", req->status,
68 req->cgi_status);
69#endif
70
71 if (bytes_read == -1) {
72 if (errno == EINTR)
73 return 1;
74 else if (errno == EWOULDBLOCK || errno == EAGAIN)
75 return -1; /* request blocked at the pipe level, but keep going */
76 else {
77 req->status = DEAD;
78 log_error_doc(req);
79 perror("pipe read");
80 return 0;
81 }
82 }
83 *(req->header_end + bytes_read) = '\0';
84
85 if (bytes_read == 0) { /* eof, write rest of buffer */
86 req->status = PIPE_WRITE;
87 if (req->cgi_status == CGI_PARSE) { /* hasn't processed header yet */
88 req->cgi_status = CGI_DONE;
89 *req->header_end = '\0'; /* points to end of read data */
90 return process_cgi_header(req); /* cgi_status will change */
91 }
92 req->cgi_status = CGI_DONE;
93 return 1;
94 }
95
96 req->header_end += bytes_read;
97
98 if (req->cgi_status != CGI_PARSE)
99 return write_from_pipe(req); /* why not try and flush the buffer now? */
100 else {
101 char *c, *buf;
102
103 buf = req->header_line;
104
105 c = strstr(buf, "\n\r\n");
106 if (c == NULL) {
107 c = strstr(buf, "\n\n");
108 if (c == NULL) {
109 return 1;
110 }
111 }
112 req->cgi_status = CGI_DONE;
113 *req->header_end = '\0'; /* points to end of read data */
114 return process_cgi_header(req); /* cgi_status will change */
115 }
116 return 1;
117}
118
119/*
120 * Name: write_from_pipe
121 * Description: Writes data previously read from a pipe
122 *
123 * Return values:
124 * -1: request blocked, move to blocked queue
125 * 0: EOF or error, close it down
126 * 1: successful write, recycle in ready queue
127 */
128
129int write_from_pipe(request * req)
130{
131 int bytes_written;
132 size_t bytes_to_write = req->header_end - req->header_line;
133
134 if (bytes_to_write == 0) {
135 if (req->cgi_status == CGI_DONE)
136 return 0;
137
138 req->status = PIPE_READ;
139 req->header_end = req->header_line = req->buffer;
140 return 1;
141 }
142
143 bytes_written = write(req->fd, req->header_line, bytes_to_write);
144
145 if (bytes_written == -1) {
146 if (errno == EWOULDBLOCK || errno == EAGAIN)
147 return -1; /* request blocked at the pipe level, but keep going */
148 else if (errno == EINTR)
149 return 1;
150 else {
151 req->status = DEAD;
152 log_error_doc(req);
153 perror("pipe write");
154 return 0;
155 }
156 }
157
158 req->header_line += bytes_written;
159 req->bytes_written += bytes_written;
160
161 /* if there won't be anything to write next time, switch state */
162 if ((unsigned) bytes_written == bytes_to_write) {
163 req->status = PIPE_READ;
164 req->header_end = req->header_line = req->buffer;
165 }
166
167 return 1;
168}
169
170#ifdef HAVE_SENDFILE
171int io_shuffle_sendfile(request * req)
172{
173 int bytes_written;
174 size_t bytes_to_write;
175 off_t sendfile_offset;
176
177 if (req->method == M_HEAD) {
178 return complete_response(req);
179 }
180
181 /* XXX trouble if range is exactly 4G on a 32-bit machine? */
182 bytes_to_write = (req->ranges->stop - req->ranges->start) + 1;
183
184 if (bytes_to_write > system_bufsize)
185 bytes_to_write = system_bufsize;
186
187retrysendfile:
188 if (bytes_to_write == 0) {
189 /* shouldn't get here, but... */
190 bytes_written = 0;
191 } else {
192 /* arg 3 of sendfile should have type "off_t *"
193 * struct range element start has type "unsigned long"
194 * Where POSIX got the idea that an offset into a file
195 * should be signed, I'll never know.
196 */
197 sendfile_offset = req->ranges->start;
198 if (sendfile_offset < 0) {
199 req->status = DEAD;
200 log_error_doc(req);
201 fprintf(stderr, "impossible offset (%lu) requested of sendfile\n",
202 req->ranges->start);
203 return 0;
204 }
205 bytes_written = sendfile(req->fd, req->data_fd,
206 &sendfile_offset,
207 bytes_to_write);
208 if (sendfile_offset < 0) {
209 req->status = DEAD;
210 log_error_doc(req);
211 fprintf(stderr,
212 "bad craziness in sendfile offset, returned %ld\n",
213 (long) sendfile_offset);
214 return 0;
215 }
216 req->ranges->start = sendfile_offset;
217 if (bytes_written < 0) {
218 if (errno == EWOULDBLOCK || errno == EAGAIN) {
219 return -1; /* request blocked at the pipe level, but keep going */
220 } else if (errno == EINTR) {
221 goto retrysendfile;
222 } else {
223 req->status = DEAD;
224#ifdef QUIET_DISCONNECT
225 if (0)
226#else
227 if (errno != EPIPE && errno != ECONNRESET)
228#endif
229 {
230 log_error_doc(req);
231 perror("sendfile write");
232 }
233 }
234 return 0;
235 } else if (bytes_written == 0) {
236 /* not sure how to handle this.
237 * For now, treat it like it is blocked.
238 */
239 return -1;
240 }/* bytes_written */
241 } /* bytes_to_write */
242
243 /* sendfile automatically updates req->ranges->start
244 * don't touch!
245 * req->ranges->start += bytes_written;
246 */
247 req->bytes_written += bytes_written;
248
249 if (req->ranges->stop + 1 <= req->ranges->start) {
250 return complete_response(req);
251 }
252 return 1;
253}
254#endif
255
256/* always try to read unless data_fs is 0 (and there is space)
257 * then try to write
258 *
259 * Return values:
260 * -1: request blocked, move to blocked queue
261 * 0: EOF or error, close it down
262 * 1: successful read, recycle in ready queue
263 */
264
265int io_shuffle(request * req)
266{
267 int bytes_to_read;
268 int bytes_written, bytes_to_write;
269
270 if (req->method == M_HEAD) {
271 return complete_response(req);
272 }
273
274 /* FIXME: This function doesn't take into account req->filesize
275 * when *reading* into the buffer. Grr.
276 * June 09, 2004: jdn, I don't think it's a problem anymore,
277 * because the ranges are verified against the filesize,
278 * and we cap bytes_to_read at bytes_to_write.
279 */
280 bytes_to_read = BUFFER_SIZE - req->buffer_end - 256;
281
282 bytes_to_write = (req->ranges->stop - req->ranges->start) + 1;
283
284 if (bytes_to_read > bytes_to_write)
285 bytes_to_read = bytes_to_write;
286
287 if (bytes_to_read > 0 && req->data_fd) {
288 int bytes_read;
289 off_t temp;
290
291 temp = lseek(req->data_fd, req->ranges->start, SEEK_SET);
292 if (temp < 0) {
293 req->status = DEAD;
294 log_error_doc(req);
295 perror("ioshuffle lseek");
296 return 0;
297 }
298
299 restartread:
300 bytes_read =
301 read(req->data_fd, req->buffer + req->buffer_end,
302 bytes_to_read);
303
304 if (bytes_read == -1) {
305 if (errno == EINTR)
306 goto restartread;
307 else if (errno == EWOULDBLOCK || errno == EAGAIN) {
308 /* not a fatal error, don't worry about it */
309 /* buffer is empty, we're blocking on read! */
310 if (req->buffer_end - req->buffer_start == 0)
311 return -1;
312 } else {
313 req->status = DEAD;
314 log_error_doc(req);
315 perror("ioshuffle read");
316 return 0;
317 }
318 } else if (bytes_read == 0) { /* eof, write rest of buffer */
319 close(req->data_fd);
320 req->data_fd = 0;
321 } else {
322 req->buffer_end += bytes_read;
323
324 req->ranges->start += bytes_read;
325
326 if ((req->ranges->stop + 1 - req->ranges->start) == 0) {
327 return complete_response(req);
328 }
329 }
330 }
331
332 bytes_to_write = req->buffer_end - req->buffer_start;
333 if (bytes_to_write == 0) {
334 if (req->data_fd == 0)
335 return 0; /* done */
336 req->buffer_end = req->buffer_start = 0;
337 return 1;
338 }
339
340 restartwrite:
341 bytes_written =
342 write(req->fd, req->buffer + req->buffer_start, bytes_to_write);
343
344 if (bytes_written == -1) {
345 if (errno == EWOULDBLOCK || errno == EAGAIN)
346 return -1; /* request blocked at the pipe level, but keep going */
347 else if (errno == EINTR)
348 goto restartwrite;
349 else {
350 req->status = DEAD;
351 log_error_doc(req);
352 perror("ioshuffle write");
353 return 0;
354 }
355 } else if (bytes_written == 0) {
356 }
357
358 req->buffer_start += bytes_written;
359 req->bytes_written += bytes_written;
360
361 if (bytes_to_write == bytes_written) {
362 req->buffer_end = req->buffer_start = 0;
363 }
364
365 return 1;
366}