~ubuntu-branches/ubuntu/trusty/nginx/trusty-proposed

« back to all changes in this revision

Viewing changes to src/os/unix/ngx_darwin_sendfile_chain.c

  • Committer: Package Import Robot
  • Author(s): Kartik Mistry
  • Date: 2013-04-25 12:51:45 UTC
  • mfrom: (1.3.28)
  • mto: (1.3.29) (15.1.2 experimental)
  • mto: This revision was merged to the branch mainline in revision 64.
  • Revision ID: package-import@ubuntu.com-20130425125145-ugl0wor6bq0u5eae
Tags: upstream-1.4.0
ImportĀ upstreamĀ versionĀ 1.4.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
/*
 
3
 * Copyright (C) Igor Sysoev
 
4
 * Copyright (C) Nginx, Inc.
 
5
 */
 
6
 
 
7
 
 
8
#include <ngx_config.h>
 
9
#include <ngx_core.h>
 
10
#include <ngx_event.h>
 
11
 
 
12
 
 
13
/*
 
14
 * It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same
 
15
 * old bug as early FreeBSD sendfile() syscall:
 
16
 * http://www.freebsd.org/cgi/query-pr.cgi?pr=33771
 
17
 *
 
18
 * Besides sendfile() has another bug: if one calls sendfile()
 
19
 * with both a header and a trailer, then sendfile() ignores a file part
 
20
 * at all and sends only the header and the trailer together.
 
21
 * For this reason we send a trailer only if there is no a header.
 
22
 *
 
23
 * Although sendfile() allows to pass a header or a trailer,
 
24
 * it may send the header or the trailer and a part of the file
 
25
 * in different packets.  And FreeBSD workaround (TCP_NOPUSH option)
 
26
 * does not help.
 
27
 */
 
28
 
 
29
 
 
30
#if (IOV_MAX > 64)
 
31
#define NGX_HEADERS   64
 
32
#define NGX_TRAILERS  64
 
33
#else
 
34
#define NGX_HEADERS   IOV_MAX
 
35
#define NGX_TRAILERS  IOV_MAX
 
36
#endif
 
37
 
 
38
 
 
39
ngx_chain_t *
 
40
ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
 
41
{
 
42
    int              rc;
 
43
    u_char          *prev;
 
44
    off_t            size, send, prev_send, aligned, sent, fprev;
 
45
    off_t            header_size, file_size;
 
46
    ngx_uint_t       eintr, complete;
 
47
    ngx_err_t        err;
 
48
    ngx_buf_t       *file;
 
49
    ngx_array_t      header, trailer;
 
50
    ngx_event_t     *wev;
 
51
    ngx_chain_t     *cl;
 
52
    struct sf_hdtr   hdtr;
 
53
    struct iovec    *iov, headers[NGX_HEADERS], trailers[NGX_TRAILERS];
 
54
 
 
55
    wev = c->write;
 
56
 
 
57
    if (!wev->ready) {
 
58
        return in;
 
59
    }
 
60
 
 
61
#if (NGX_HAVE_KQUEUE)
 
62
 
 
63
    if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
 
64
        (void) ngx_connection_error(c, wev->kq_errno,
 
65
                               "kevent() reported about an closed connection");
 
66
        wev->error = 1;
 
67
        return NGX_CHAIN_ERROR;
 
68
    }
 
69
 
 
70
#endif
 
71
 
 
72
    /* the maximum limit size is the maximum size_t value - the page size */
 
73
 
 
74
    if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
 
75
        limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
 
76
    }
 
77
 
 
78
    send = 0;
 
79
 
 
80
    header.elts = headers;
 
81
    header.size = sizeof(struct iovec);
 
82
    header.nalloc = NGX_HEADERS;
 
83
    header.pool = c->pool;
 
84
 
 
85
    trailer.elts = trailers;
 
86
    trailer.size = sizeof(struct iovec);
 
87
    trailer.nalloc = NGX_TRAILERS;
 
88
    trailer.pool = c->pool;
 
89
 
 
90
    for ( ;; ) {
 
91
        file = NULL;
 
92
        file_size = 0;
 
93
        header_size = 0;
 
94
        eintr = 0;
 
95
        complete = 0;
 
96
        prev_send = send;
 
97
 
 
98
        header.nelts = 0;
 
99
        trailer.nelts = 0;
 
100
 
 
101
        /* create the header iovec and coalesce the neighbouring bufs */
 
102
 
 
103
        prev = NULL;
 
104
        iov = NULL;
 
105
 
 
106
        for (cl = in; cl && send < limit; cl = cl->next) {
 
107
 
 
108
            if (ngx_buf_special(cl->buf)) {
 
109
                continue;
 
110
            }
 
111
 
 
112
            if (!ngx_buf_in_memory_only(cl->buf)) {
 
113
                break;
 
114
            }
 
115
 
 
116
            size = cl->buf->last - cl->buf->pos;
 
117
 
 
118
            if (send + size > limit) {
 
119
                size = limit - send;
 
120
            }
 
121
 
 
122
            if (prev == cl->buf->pos) {
 
123
                iov->iov_len += (size_t) size;
 
124
 
 
125
            } else {
 
126
                if (header.nelts >= IOV_MAX) {
 
127
                    break;
 
128
                }
 
129
 
 
130
                iov = ngx_array_push(&header);
 
131
                if (iov == NULL) {
 
132
                    return NGX_CHAIN_ERROR;
 
133
                }
 
134
 
 
135
                iov->iov_base = (void *) cl->buf->pos;
 
136
                iov->iov_len = (size_t) size;
 
137
            }
 
138
 
 
139
            prev = cl->buf->pos + (size_t) size;
 
140
            header_size += size;
 
141
            send += size;
 
142
        }
 
143
 
 
144
 
 
145
        if (cl && cl->buf->in_file && send < limit) {
 
146
            file = cl->buf;
 
147
 
 
148
            /* coalesce the neighbouring file bufs */
 
149
 
 
150
            do {
 
151
                size = cl->buf->file_last - cl->buf->file_pos;
 
152
 
 
153
                if (send + size > limit) {
 
154
                    size = limit - send;
 
155
 
 
156
                    aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
 
157
                               & ~((off_t) ngx_pagesize - 1);
 
158
 
 
159
                    if (aligned <= cl->buf->file_last) {
 
160
                        size = aligned - cl->buf->file_pos;
 
161
                    }
 
162
                }
 
163
 
 
164
                file_size += size;
 
165
                send += size;
 
166
                fprev = cl->buf->file_pos + size;
 
167
                cl = cl->next;
 
168
 
 
169
            } while (cl
 
170
                     && cl->buf->in_file
 
171
                     && send < limit
 
172
                     && file->file->fd == cl->buf->file->fd
 
173
                     && fprev == cl->buf->file_pos);
 
174
        }
 
175
 
 
176
        if (file && header.nelts == 0) {
 
177
 
 
178
            /* create the trailer iovec and coalesce the neighbouring bufs */
 
179
 
 
180
            prev = NULL;
 
181
            iov = NULL;
 
182
 
 
183
            while (cl && send < limit) {
 
184
 
 
185
                if (ngx_buf_special(cl->buf)) {
 
186
                    cl = cl->next;
 
187
                    continue;
 
188
                }
 
189
 
 
190
                if (!ngx_buf_in_memory_only(cl->buf)) {
 
191
                    break;
 
192
                }
 
193
 
 
194
                size = cl->buf->last - cl->buf->pos;
 
195
 
 
196
                if (send + size > limit) {
 
197
                    size = limit - send;
 
198
                }
 
199
 
 
200
                if (prev == cl->buf->pos) {
 
201
                    iov->iov_len += (size_t) size;
 
202
 
 
203
                } else {
 
204
                    if (trailer.nelts >= IOV_MAX) {
 
205
                        break;
 
206
                    }
 
207
 
 
208
                    iov = ngx_array_push(&trailer);
 
209
                    if (iov == NULL) {
 
210
                        return NGX_CHAIN_ERROR;
 
211
                    }
 
212
 
 
213
                    iov->iov_base = (void *) cl->buf->pos;
 
214
                    iov->iov_len = (size_t) size;
 
215
                }
 
216
 
 
217
                prev = cl->buf->pos + (size_t) size;
 
218
                send += size;
 
219
                cl = cl->next;
 
220
            }
 
221
        }
 
222
 
 
223
        if (file) {
 
224
 
 
225
            /*
 
226
             * sendfile() returns EINVAL if sf_hdtr's count is 0,
 
227
             * but corresponding pointer is not NULL
 
228
             */
 
229
 
 
230
            hdtr.headers = header.nelts ? (struct iovec *) header.elts: NULL;
 
231
            hdtr.hdr_cnt = header.nelts;
 
232
            hdtr.trailers = trailer.nelts ? (struct iovec *) trailer.elts: NULL;
 
233
            hdtr.trl_cnt = trailer.nelts;
 
234
 
 
235
            sent = header_size + file_size;
 
236
 
 
237
            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
 
238
                           "sendfile: @%O %O h:%O",
 
239
                           file->file_pos, sent, header_size);
 
240
 
 
241
            rc = sendfile(file->file->fd, c->fd, file->file_pos,
 
242
                          &sent, &hdtr, 0);
 
243
 
 
244
            if (rc == -1) {
 
245
                err = ngx_errno;
 
246
 
 
247
                switch (err) {
 
248
                case NGX_EAGAIN:
 
249
                    break;
 
250
 
 
251
                case NGX_EINTR:
 
252
                    eintr = 1;
 
253
                    break;
 
254
 
 
255
                default:
 
256
                    wev->error = 1;
 
257
                    (void) ngx_connection_error(c, err, "sendfile() failed");
 
258
                    return NGX_CHAIN_ERROR;
 
259
                }
 
260
 
 
261
                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
 
262
                               "sendfile() sent only %O bytes", sent);
 
263
            }
 
264
 
 
265
            if (rc == 0 && sent == 0) {
 
266
 
 
267
                /*
 
268
                 * if rc and sent equal to zero, then someone
 
269
                 * has truncated the file, so the offset became beyond
 
270
                 * the end of the file
 
271
                 */
 
272
 
 
273
                ngx_log_error(NGX_LOG_ALERT, c->log, 0,
 
274
                              "sendfile() reported that \"%s\" was truncated",
 
275
                              file->file->name.data);
 
276
 
 
277
                return NGX_CHAIN_ERROR;
 
278
            }
 
279
 
 
280
            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
 
281
                           "sendfile: %d, @%O %O:%O",
 
282
                           rc, file->file_pos, sent, file_size + header_size);
 
283
 
 
284
        } else {
 
285
            rc = writev(c->fd, header.elts, header.nelts);
 
286
 
 
287
            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
 
288
                           "writev: %d of %uz", rc, send);
 
289
 
 
290
            if (rc == -1) {
 
291
                err = ngx_errno;
 
292
 
 
293
                switch (err) {
 
294
                case NGX_EAGAIN:
 
295
                    break;
 
296
 
 
297
                case NGX_EINTR:
 
298
                    eintr = 1;
 
299
                    break;
 
300
 
 
301
                default:
 
302
                    wev->error = 1;
 
303
                    ngx_connection_error(c, err, "writev() failed");
 
304
                    return NGX_CHAIN_ERROR;
 
305
                }
 
306
 
 
307
                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
 
308
                               "writev() not ready");
 
309
            }
 
310
 
 
311
            sent = rc > 0 ? rc : 0;
 
312
        }
 
313
 
 
314
        if (send - prev_send == sent) {
 
315
            complete = 1;
 
316
        }
 
317
 
 
318
        c->sent += sent;
 
319
 
 
320
        for (cl = in; cl; cl = cl->next) {
 
321
 
 
322
            if (ngx_buf_special(cl->buf)) {
 
323
                continue;
 
324
            }
 
325
 
 
326
            if (sent == 0) {
 
327
                break;
 
328
            }
 
329
 
 
330
            size = ngx_buf_size(cl->buf);
 
331
 
 
332
            if (sent >= size) {
 
333
                sent -= size;
 
334
 
 
335
                if (ngx_buf_in_memory(cl->buf)) {
 
336
                    cl->buf->pos = cl->buf->last;
 
337
                }
 
338
 
 
339
                if (cl->buf->in_file) {
 
340
                    cl->buf->file_pos = cl->buf->file_last;
 
341
                }
 
342
 
 
343
                continue;
 
344
            }
 
345
 
 
346
            if (ngx_buf_in_memory(cl->buf)) {
 
347
                cl->buf->pos += (size_t) sent;
 
348
            }
 
349
 
 
350
            if (cl->buf->in_file) {
 
351
                cl->buf->file_pos += sent;
 
352
            }
 
353
 
 
354
            break;
 
355
        }
 
356
 
 
357
        if (eintr) {
 
358
            continue;
 
359
        }
 
360
 
 
361
        if (!complete) {
 
362
            wev->ready = 0;
 
363
            return cl;
 
364
        }
 
365
 
 
366
        if (send >= limit || cl == NULL) {
 
367
            return cl;
 
368
        }
 
369
 
 
370
        in = cl;
 
371
    }
 
372
}