Annotation of embedaddon/nginx/src/os/unix/ngx_darwin_sendfile_chain.c, revision 1.1
1.1 ! misho 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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>