summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_freebsd_sendfile.c
blob: a9535c1094b834d0062a45695e44a70c7cca3500 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143

/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) NGINX, Inc.
 */

#include <nxt_main.h>


/*
 * sendfile() has been introduced in FreeBSD 3.1,
 * however, early implementation had various bugs.
 * This code supports FreeBSD 5.0 implementation.
 */

#ifdef NXT_TEST_BUILD_FREEBSD_SENDFILE

ssize_t nxt_freebsd_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b,
    size_t limit);

static int nxt_sys_sendfile(int fd, int s, off_t offset, size_t nbytes,
    struct sf_hdtr *hdtr, off_t *sbytes, int flags)
{
    return -1;
}

#else
#define nxt_sys_sendfile  sendfile
#endif


ssize_t
nxt_freebsd_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b,
    size_t limit)
{
    size_t                  file_size;
    ssize_t                 n;
    nxt_buf_t               *fb;
    nxt_err_t               err;
    nxt_off_t               sent;
    nxt_uint_t              nhd, ntr;
    struct iovec            hd[NXT_IOBUF_MAX], tr[NXT_IOBUF_MAX];
    struct sf_hdtr          hdtr, *ht;
    nxt_sendbuf_coalesce_t  sb;

    sb.buf = b;
    sb.iobuf = hd;
    sb.nmax = NXT_IOBUF_MAX;
    sb.sync = 0;
    sb.size = 0;
    sb.limit = limit;

    nhd = nxt_sendbuf_mem_coalesce(c->socket.task, &sb);

    if (nhd == 0 && sb.sync) {
        return 0;
    }

    if (sb.buf == NULL || !nxt_buf_is_file(sb.buf)) {
        return nxt_event_conn_io_writev(c, hd, nhd);
    }

    fb = sb.buf;

    file_size = nxt_sendbuf_file_coalesce(&sb);

    if (file_size == 0) {
        return nxt_event_conn_io_writev(c, hd, nhd);
    }

    sb.iobuf = tr;

    ntr = nxt_sendbuf_mem_coalesce(c->socket.task, &sb);

    /*
     * Disposal of surplus kernel operations
     * if there are no headers or trailers.
     */

    ht = NULL;
    nxt_memzero(&hdtr, sizeof(struct sf_hdtr));

    if (nhd != 0) {
        ht = &hdtr;
        hdtr.headers = hd;
        hdtr.hdr_cnt = nhd;
    }

    if (ntr != 0) {
        ht = &hdtr;
        hdtr.trailers = tr;
        hdtr.trl_cnt = ntr;
    }

    nxt_debug(c->socket.task, "sendfile(%FD, %d, @%O, %uz) hd:%ui tr:%ui",
                  fb->file->fd, c->socket.fd, fb->file_pos, file_size,
                  nhd, ntr);

    sent = 0;
    n = nxt_sys_sendfile(fb->file->fd, c->socket.fd, fb->file_pos,
                         file_size, ht, &sent, 0);

    err = (n == -1) ? nxt_errno : 0;

    nxt_debug(c->socket.task, "sendfile(): %d sent:%O", n, sent);

    if (n == -1) {
        switch (err) {

        case NXT_EAGAIN:
            c->socket.write_ready = 0;
            break;

        case NXT_EINTR:
            break;

        default:
            c->socket.error = err;
            nxt_log(c->socket.task, nxt_socket_error_level(err),
                "sendfile(%FD, %d, %O, %uz) failed %E \"%FN\" hd:%ui tr:%ui",
                fb->file->fd, c->socket.fd, fb->file_pos, file_size, err,
                fb->file->name, nhd, ntr);

            return NXT_ERROR;
        }

        nxt_debug(c->socket.task, "sendfile() %E", err);

        return sent;

    } else if (sent == 0) {
        nxt_log(c->socket.task, NXT_LOG_ERR,
                "file \"%FN\" was truncated while sendfile()", fb->file->name);

        return NXT_ERROR;
    }

    if (sent < (nxt_off_t) sb.size) {
        c->socket.write_ready = 0;
    }

    return sent;
}