10Sigor@sysoev.ru 20Sigor@sysoev.ru /* 30Sigor@sysoev.ru * Copyright (C) Igor Sysoev 40Sigor@sysoev.ru * Copyright (C) NGINX, Inc. 50Sigor@sysoev.ru */ 60Sigor@sysoev.ru 70Sigor@sysoev.ru #include <nxt_main.h> 80Sigor@sysoev.ru 90Sigor@sysoev.ru 100Sigor@sysoev.ru /* sendfile() has been introduced in MacOSX 10.5 (Leopard) */ 110Sigor@sysoev.ru 120Sigor@sysoev.ru #ifdef NXT_TEST_BUILD_MACOSX_SENDFILE 130Sigor@sysoev.ru 140Sigor@sysoev.ru ssize_t nxt_macosx_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b, 150Sigor@sysoev.ru size_t limit); 160Sigor@sysoev.ru 170Sigor@sysoev.ru static int nxt_sys_sendfile(int fd, int s, off_t offset, off_t *len, 180Sigor@sysoev.ru struct sf_hdtr *hdtr, int flags) 190Sigor@sysoev.ru { 200Sigor@sysoev.ru return -1; 210Sigor@sysoev.ru } 220Sigor@sysoev.ru 230Sigor@sysoev.ru #else 240Sigor@sysoev.ru #define nxt_sys_sendfile sendfile 250Sigor@sysoev.ru #endif 260Sigor@sysoev.ru 270Sigor@sysoev.ru 280Sigor@sysoev.ru ssize_t 290Sigor@sysoev.ru nxt_macosx_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b, 300Sigor@sysoev.ru size_t limit) 310Sigor@sysoev.ru { 320Sigor@sysoev.ru size_t hd_size, file_size; 330Sigor@sysoev.ru ssize_t n; 340Sigor@sysoev.ru nxt_buf_t *fb; 350Sigor@sysoev.ru nxt_err_t err; 360Sigor@sysoev.ru nxt_off_t sent; 370Sigor@sysoev.ru nxt_uint_t nhd, ntr; 380Sigor@sysoev.ru struct iovec hd[NXT_IOBUF_MAX], tr[NXT_IOBUF_MAX]; 390Sigor@sysoev.ru struct sf_hdtr hdtr, *ht; 400Sigor@sysoev.ru nxt_sendbuf_coalesce_t sb; 410Sigor@sysoev.ru 420Sigor@sysoev.ru sb.buf = b; 430Sigor@sysoev.ru sb.iobuf = hd; 440Sigor@sysoev.ru sb.nmax = NXT_IOBUF_MAX; 450Sigor@sysoev.ru sb.sync = 0; 460Sigor@sysoev.ru sb.size = 0; 470Sigor@sysoev.ru sb.limit = limit; 480Sigor@sysoev.ru 49*1Sigor@sysoev.ru nhd = nxt_sendbuf_mem_coalesce(c->socket.task, &sb); 500Sigor@sysoev.ru 510Sigor@sysoev.ru if (nhd == 0 && sb.sync) { 520Sigor@sysoev.ru return 0; 530Sigor@sysoev.ru } 540Sigor@sysoev.ru 550Sigor@sysoev.ru if (sb.buf == NULL || !nxt_buf_is_file(sb.buf)) { 560Sigor@sysoev.ru return nxt_event_conn_io_writev(c, hd, nhd); 570Sigor@sysoev.ru } 580Sigor@sysoev.ru 590Sigor@sysoev.ru hd_size = sb.size; 600Sigor@sysoev.ru fb = sb.buf; 610Sigor@sysoev.ru 620Sigor@sysoev.ru file_size = nxt_sendbuf_file_coalesce(&sb); 630Sigor@sysoev.ru 640Sigor@sysoev.ru if (file_size == 0) { 650Sigor@sysoev.ru return nxt_event_conn_io_writev(c, hd, nhd); 660Sigor@sysoev.ru } 670Sigor@sysoev.ru 680Sigor@sysoev.ru sb.iobuf = tr; 690Sigor@sysoev.ru 70*1Sigor@sysoev.ru ntr = nxt_sendbuf_mem_coalesce(c->socket.task, &sb); 710Sigor@sysoev.ru 720Sigor@sysoev.ru /* 730Sigor@sysoev.ru * Disposal of surplus kernel operations if there are no headers 740Sigor@sysoev.ru * and trailers. Besides sendfile() returns EINVAL if a sf_hdtr's 750Sigor@sysoev.ru * count is 0, but corresponding pointer is not NULL. 760Sigor@sysoev.ru */ 770Sigor@sysoev.ru 780Sigor@sysoev.ru nxt_memzero(&hdtr, sizeof(struct sf_hdtr)); 790Sigor@sysoev.ru ht = NULL; 800Sigor@sysoev.ru 810Sigor@sysoev.ru if (nhd != 0) { 820Sigor@sysoev.ru ht = &hdtr; 830Sigor@sysoev.ru hdtr.headers = hd; 840Sigor@sysoev.ru hdtr.hdr_cnt = nhd; 850Sigor@sysoev.ru } 860Sigor@sysoev.ru 870Sigor@sysoev.ru if (ntr != 0) { 880Sigor@sysoev.ru ht = &hdtr; 890Sigor@sysoev.ru hdtr.trailers = tr; 900Sigor@sysoev.ru hdtr.trl_cnt = ntr; 910Sigor@sysoev.ru } 920Sigor@sysoev.ru 930Sigor@sysoev.ru /* 940Sigor@sysoev.ru * MacOSX has the same bug as old FreeBSD (http://bugs.freebsd.org/33771). 950Sigor@sysoev.ru * However this bug has never been fixed and instead of this it has been 960Sigor@sysoev.ru * documented as a feature in MacOSX 10.7 (Lion) sendfile(2): 970Sigor@sysoev.ru * 980Sigor@sysoev.ru * When a header or trailer is specified, the value of len argument 990Sigor@sysoev.ru * indicates the maximum number of bytes in the header and/or file 1000Sigor@sysoev.ru * to be sent. It does not control the trailer; if a trailer exists, 1010Sigor@sysoev.ru * all of it will be sent. 1020Sigor@sysoev.ru */ 1030Sigor@sysoev.ru sent = hd_size + file_size; 1040Sigor@sysoev.ru 1050Sigor@sysoev.ru nxt_log_debug(c->socket.log, 1060Sigor@sysoev.ru "sendfile(%FD, %d, @%O, %O) hd:%ui tr:%ui hs:%uz", 1070Sigor@sysoev.ru fb->file->fd, c->socket.fd, fb->file_pos, sent, 1080Sigor@sysoev.ru nhd, ntr, hd_size); 1090Sigor@sysoev.ru 1100Sigor@sysoev.ru n = nxt_sys_sendfile(fb->file->fd, c->socket.fd, 1110Sigor@sysoev.ru fb->file_pos, &sent, ht, 0); 1120Sigor@sysoev.ru 1130Sigor@sysoev.ru err = (n == -1) ? nxt_errno : 0; 1140Sigor@sysoev.ru 1150Sigor@sysoev.ru nxt_log_debug(c->socket.log, "sendfile(): %d sent:%O", n, sent); 1160Sigor@sysoev.ru 1170Sigor@sysoev.ru if (n == -1) { 1180Sigor@sysoev.ru switch (err) { 1190Sigor@sysoev.ru 1200Sigor@sysoev.ru case NXT_EAGAIN: 1210Sigor@sysoev.ru c->socket.write_ready = 0; 1220Sigor@sysoev.ru break; 1230Sigor@sysoev.ru 1240Sigor@sysoev.ru case NXT_EINTR: 1250Sigor@sysoev.ru break; 1260Sigor@sysoev.ru 1270Sigor@sysoev.ru default: 1280Sigor@sysoev.ru c->socket.error = err; 1290Sigor@sysoev.ru nxt_log_error(nxt_socket_error_level(err, c->socket.log_error), 1300Sigor@sysoev.ru c->socket.log, "sendfile(%FD, %d, %O, %O) failed " 1310Sigor@sysoev.ru "%E \"%FN\" hd:%ui tr:%ui", fb->file->fd, 1320Sigor@sysoev.ru c->socket.fd, fb->file_pos, sent, err, 1330Sigor@sysoev.ru fb->file->name, nhd, ntr); 1340Sigor@sysoev.ru 1350Sigor@sysoev.ru return NXT_ERROR; 1360Sigor@sysoev.ru } 1370Sigor@sysoev.ru 1380Sigor@sysoev.ru nxt_log_debug(c->socket.log, "sendfile() %E", err); 1390Sigor@sysoev.ru 1400Sigor@sysoev.ru return sent; 1410Sigor@sysoev.ru } 1420Sigor@sysoev.ru 1430Sigor@sysoev.ru if (sent == 0) { 1440Sigor@sysoev.ru nxt_log_error(NXT_LOG_ERR, c->socket.log, 1450Sigor@sysoev.ru "file \"%FN\" was truncated while sendfile()", 1460Sigor@sysoev.ru fb->file->name); 1470Sigor@sysoev.ru 1480Sigor@sysoev.ru return NXT_ERROR; 1490Sigor@sysoev.ru } 1500Sigor@sysoev.ru 1510Sigor@sysoev.ru if (sent < (nxt_off_t) sb.size) { 1520Sigor@sysoev.ru c->socket.write_ready = 0; 1530Sigor@sysoev.ru } 1540Sigor@sysoev.ru 1550Sigor@sysoev.ru return sent; 1560Sigor@sysoev.ru } 1570Sigor@sysoev.ru 1580Sigor@sysoev.ru 1590Sigor@sysoev.ru #if 0 1600Sigor@sysoev.ru 1610Sigor@sysoev.ru typedef struct { 1620Sigor@sysoev.ru nxt_socket_t socket; 1630Sigor@sysoev.ru nxt_err_t error; 1640Sigor@sysoev.ru 1650Sigor@sysoev.ru uint8_t write_ready; /* 1 bit */ 1660Sigor@sysoev.ru uint8_t log_error; 1670Sigor@sysoev.ru } nxt_sendbuf_t; 1680Sigor@sysoev.ru 1690Sigor@sysoev.ru 1700Sigor@sysoev.ru ssize_t nxt_macosx_sendfile(nxt_thread_t *thr, nxt_sendbuf_t *sb, nxt_buf_t *b, 1710Sigor@sysoev.ru size_t limit); 1720Sigor@sysoev.ru ssize_t nxt_writev(nxt_thread_t *thr, nxt_sendbuf_t *sb, nxt_iobuf_t *iob, 1730Sigor@sysoev.ru nxt_uint_t niob); 1740Sigor@sysoev.ru ssize_t nxt_send(nxt_thread_t *thr, nxt_sendbuf_t *sb, void *buf, size_t size); 1750Sigor@sysoev.ru 1760Sigor@sysoev.ru 1770Sigor@sysoev.ru ssize_t 1780Sigor@sysoev.ru nxt_macosx_sendfile(nxt_thread_t *thr, nxt_sendbuf_t *sb, nxt_buf_t *b, 1790Sigor@sysoev.ru size_t limit) 1800Sigor@sysoev.ru { 1810Sigor@sysoev.ru size_t hd_size, file_size; 1820Sigor@sysoev.ru ssize_t n; 1830Sigor@sysoev.ru nxt_buf_t *buf; 1840Sigor@sysoev.ru nxt_err_t err; 1850Sigor@sysoev.ru nxt_off_t sent; 1860Sigor@sysoev.ru nxt_uint_t nhd, ntr; 1870Sigor@sysoev.ru struct iovec hd[NXT_IOBUF_MAX], tr[NXT_IOBUF_MAX]; 1880Sigor@sysoev.ru struct sf_hdtr hdtr, *ht; 1890Sigor@sysoev.ru nxt_sendbuf_coalesce_t sbc; 1900Sigor@sysoev.ru 1910Sigor@sysoev.ru sbc.buf = b; 1920Sigor@sysoev.ru sbc.iobuf = hd; 1930Sigor@sysoev.ru sbc.nmax = NXT_IOBUF_MAX; 1940Sigor@sysoev.ru sbc.sync = 0; 1950Sigor@sysoev.ru sbc.size = 0; 1960Sigor@sysoev.ru sbc.limit = limit; 1970Sigor@sysoev.ru 1980Sigor@sysoev.ru nhd = nxt_sendbuf_mem_coalesce(&sbc); 1990Sigor@sysoev.ru 2000Sigor@sysoev.ru if (nhd == 0 && sbc.sync) { 2010Sigor@sysoev.ru return 0; 2020Sigor@sysoev.ru } 2030Sigor@sysoev.ru 2040Sigor@sysoev.ru if (sbc.buf == NULL || !nxt_buf_is_file(sbc.buf)) { 2050Sigor@sysoev.ru return nxt_writev(thr, sb, hd, nhd); 2060Sigor@sysoev.ru } 2070Sigor@sysoev.ru 2080Sigor@sysoev.ru hd_size = sbc.size; 2090Sigor@sysoev.ru buf = sbc.buf; 2100Sigor@sysoev.ru 2110Sigor@sysoev.ru file_size = nxt_sendbuf_file_coalesce(&sbc); 2120Sigor@sysoev.ru 2130Sigor@sysoev.ru if (file_size == 0) { 2140Sigor@sysoev.ru return nxt_writev(thr, sb, hd, nhd); 2150Sigor@sysoev.ru } 2160Sigor@sysoev.ru 2170Sigor@sysoev.ru sbc.iobuf = tr; 2180Sigor@sysoev.ru 2190Sigor@sysoev.ru ntr = nxt_sendbuf_mem_coalesce(&sbc); 2200Sigor@sysoev.ru 2210Sigor@sysoev.ru /* 2220Sigor@sysoev.ru * Disposal of surplus kernel operations if there are no headers 2230Sigor@sysoev.ru * and trailers. Besides sendfile() returns EINVAL if a sf_hdtr's 2240Sigor@sysoev.ru * count is 0, but corresponding pointer is not NULL. 2250Sigor@sysoev.ru */ 2260Sigor@sysoev.ru 2270Sigor@sysoev.ru nxt_memzero(&hdtr, sizeof(struct sf_hdtr)); 2280Sigor@sysoev.ru ht = NULL; 2290Sigor@sysoev.ru 2300Sigor@sysoev.ru if (nhd != 0) { 2310Sigor@sysoev.ru ht = &hdtr; 2320Sigor@sysoev.ru hdtr.headers = hd; 2330Sigor@sysoev.ru hdtr.hdr_cnt = nhd; 2340Sigor@sysoev.ru } 2350Sigor@sysoev.ru 2360Sigor@sysoev.ru if (ntr != 0) { 2370Sigor@sysoev.ru ht = &hdtr; 2380Sigor@sysoev.ru hdtr.trailers = tr; 2390Sigor@sysoev.ru hdtr.trl_cnt = ntr; 2400Sigor@sysoev.ru } 2410Sigor@sysoev.ru 2420Sigor@sysoev.ru /* 2430Sigor@sysoev.ru * MacOSX has the same bug as old FreeBSD (http://bugs.freebsd.org/33771). 2440Sigor@sysoev.ru * However this bug has never been fixed and instead of this it has been 2450Sigor@sysoev.ru * documented as a feature in MacOSX 10.7 (Lion) sendfile(2): 2460Sigor@sysoev.ru * 2470Sigor@sysoev.ru * When a header or trailer is specified, the value of len argument 2480Sigor@sysoev.ru * indicates the maximum number of bytes in the header and/or file 2490Sigor@sysoev.ru * to be sent. It does not control the trailer; if a trailer exists, 2500Sigor@sysoev.ru * all of it will be sent. 2510Sigor@sysoev.ru */ 2520Sigor@sysoev.ru sent = hd_size + file_size; 2530Sigor@sysoev.ru 2540Sigor@sysoev.ru nxt_log_debug(thr->log, "sendfile(%FD, %d, @%O, %O) hd:%ui tr:%ui hs:%uz", 2550Sigor@sysoev.ru buf->file->fd, sb->socket, buf->file_pos, sent, 2560Sigor@sysoev.ru nhd, ntr, hd_size); 2570Sigor@sysoev.ru 2580Sigor@sysoev.ru n = nxt_sys_sendfile(buf->file->fd, sb->socket, 2590Sigor@sysoev.ru buf->file_pos, &sent, ht, 0); 2600Sigor@sysoev.ru 2610Sigor@sysoev.ru err = (n == -1) ? nxt_errno : 0; 2620Sigor@sysoev.ru 2630Sigor@sysoev.ru nxt_log_debug(thr->log, "sendfile(): %d sent:%O", n, sent); 2640Sigor@sysoev.ru 2650Sigor@sysoev.ru if (n == -1) { 2660Sigor@sysoev.ru switch (err) { 2670Sigor@sysoev.ru 2680Sigor@sysoev.ru case NXT_EAGAIN: 2690Sigor@sysoev.ru sb->write_ready = 0; 2700Sigor@sysoev.ru break; 2710Sigor@sysoev.ru 2720Sigor@sysoev.ru case NXT_EINTR: 2730Sigor@sysoev.ru break; 2740Sigor@sysoev.ru 2750Sigor@sysoev.ru default: 2760Sigor@sysoev.ru sb->error = err; 2770Sigor@sysoev.ru nxt_log_error(nxt_socket_error_level(err, sb->log_error), thr->log, 2780Sigor@sysoev.ru "sendfile(%FD, %d, %O, %O) failed %E \"%FN\" " 2790Sigor@sysoev.ru "hd:%ui tr:%ui", buf->file->fd, sb->socket, 2800Sigor@sysoev.ru buf->file_pos, sent, err, buf->file->name, nhd, ntr); 2810Sigor@sysoev.ru 2820Sigor@sysoev.ru return NXT_ERROR; 2830Sigor@sysoev.ru } 2840Sigor@sysoev.ru 2850Sigor@sysoev.ru nxt_log_debug(thr->log, "sendfile() %E", err); 2860Sigor@sysoev.ru 2870Sigor@sysoev.ru return sent; 2880Sigor@sysoev.ru } 2890Sigor@sysoev.ru 2900Sigor@sysoev.ru if (sent == 0) { 2910Sigor@sysoev.ru nxt_log_error(NXT_LOG_ERR, thr->log, 2920Sigor@sysoev.ru "file \"%FN\" was truncated while sendfile()", 2930Sigor@sysoev.ru buf->file->name); 2940Sigor@sysoev.ru 2950Sigor@sysoev.ru return NXT_ERROR; 2960Sigor@sysoev.ru } 2970Sigor@sysoev.ru 2980Sigor@sysoev.ru if (sent < (nxt_off_t) sbc.size) { 2990Sigor@sysoev.ru sb->write_ready = 0; 3000Sigor@sysoev.ru } 3010Sigor@sysoev.ru 3020Sigor@sysoev.ru return sent; 3030Sigor@sysoev.ru } 3040Sigor@sysoev.ru 3050Sigor@sysoev.ru 3060Sigor@sysoev.ru ssize_t 3070Sigor@sysoev.ru nxt_writev(nxt_thread_t *thr, nxt_sendbuf_t *sb, nxt_iobuf_t *iob, 3080Sigor@sysoev.ru nxt_uint_t niob) 3090Sigor@sysoev.ru { 3100Sigor@sysoev.ru ssize_t n; 3110Sigor@sysoev.ru nxt_err_t err; 3120Sigor@sysoev.ru 3130Sigor@sysoev.ru if (niob == 1) { 3140Sigor@sysoev.ru /* Disposal of surplus kernel iovec copy-in operation. */ 3150Sigor@sysoev.ru return nxt_send(thr, sb, iob->iov_base, iob->iov_len); 3160Sigor@sysoev.ru } 3170Sigor@sysoev.ru 3180Sigor@sysoev.ru for ( ;; ) { 3190Sigor@sysoev.ru n = writev(sb->socket, iob, niob); 3200Sigor@sysoev.ru 3210Sigor@sysoev.ru err = (n == -1) ? nxt_socket_errno : 0; 3220Sigor@sysoev.ru 3230Sigor@sysoev.ru nxt_log_debug(thr->log, "writev(%d, %ui): %d", sb->socket, niob, n); 3240Sigor@sysoev.ru 3250Sigor@sysoev.ru if (n > 0) { 3260Sigor@sysoev.ru return n; 3270Sigor@sysoev.ru } 3280Sigor@sysoev.ru 3290Sigor@sysoev.ru /* n == -1 */ 3300Sigor@sysoev.ru 3310Sigor@sysoev.ru switch (err) { 3320Sigor@sysoev.ru 3330Sigor@sysoev.ru case NXT_EAGAIN: 3340Sigor@sysoev.ru nxt_log_debug(thr->log, "writev() %E", err); 3350Sigor@sysoev.ru sb->write_ready = 0; 3360Sigor@sysoev.ru return NXT_AGAIN; 3370Sigor@sysoev.ru 3380Sigor@sysoev.ru case NXT_EINTR: 3390Sigor@sysoev.ru nxt_log_debug(thr->log, "writev() %E", err); 3400Sigor@sysoev.ru continue; 3410Sigor@sysoev.ru 3420Sigor@sysoev.ru default: 3430Sigor@sysoev.ru sb->error = err; 3440Sigor@sysoev.ru nxt_log_error(nxt_socket_error_level(err, sb->log_error), thr->log, 3450Sigor@sysoev.ru "writev(%d, %ui) failed %E", sb->socket, niob, err); 3460Sigor@sysoev.ru return NXT_ERROR; 3470Sigor@sysoev.ru } 3480Sigor@sysoev.ru } 3490Sigor@sysoev.ru } 3500Sigor@sysoev.ru 3510Sigor@sysoev.ru 3520Sigor@sysoev.ru ssize_t 3530Sigor@sysoev.ru nxt_send(nxt_thread_t *thr, nxt_sendbuf_t *sb, void *buf, size_t size) 3540Sigor@sysoev.ru { 3550Sigor@sysoev.ru ssize_t n; 3560Sigor@sysoev.ru nxt_err_t err; 3570Sigor@sysoev.ru 3580Sigor@sysoev.ru for ( ;; ) { 3590Sigor@sysoev.ru n = send(sb->socket, buf, size, 0); 3600Sigor@sysoev.ru 3610Sigor@sysoev.ru err = (n == -1) ? nxt_socket_errno : 0; 3620Sigor@sysoev.ru 3630Sigor@sysoev.ru nxt_log_debug(thr->log, "send(%d, %p, %uz): %z", 3640Sigor@sysoev.ru sb->socket, buf, size, n); 3650Sigor@sysoev.ru 3660Sigor@sysoev.ru if (n > 0) { 3670Sigor@sysoev.ru return n; 3680Sigor@sysoev.ru } 3690Sigor@sysoev.ru 3700Sigor@sysoev.ru /* n == -1 */ 3710Sigor@sysoev.ru 3720Sigor@sysoev.ru switch (err) { 3730Sigor@sysoev.ru 3740Sigor@sysoev.ru case NXT_EAGAIN: 3750Sigor@sysoev.ru nxt_log_debug(thr->log, "send() %E", err); 3760Sigor@sysoev.ru sb->write_ready = 0; 3770Sigor@sysoev.ru return NXT_AGAIN; 3780Sigor@sysoev.ru 3790Sigor@sysoev.ru case NXT_EINTR: 3800Sigor@sysoev.ru nxt_log_debug(thr->log, "send() %E", err); 3810Sigor@sysoev.ru continue; 3820Sigor@sysoev.ru 3830Sigor@sysoev.ru default: 3840Sigor@sysoev.ru sb->error = err; 3850Sigor@sysoev.ru nxt_log_error(nxt_socket_error_level(err, sb->log_error), thr->log, 3860Sigor@sysoev.ru "send(%d, %p, %uz) failed %E", 3870Sigor@sysoev.ru sb->socket, buf, size, err); 3880Sigor@sysoev.ru return NXT_ERROR; 3890Sigor@sysoev.ru } 3900Sigor@sysoev.ru } 3910Sigor@sysoev.ru } 3920Sigor@sysoev.ru 3930Sigor@sysoev.ru #endif 394