ngx_wsasend_chain.c revision e18a033b
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#define NGX_WSABUFS  8
14
15
16ngx_chain_t *
17ngx_wsasend_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
18{
19    int           rc;
20    u_char       *prev;
21    u_long        size, sent, send, prev_send;
22    ngx_err_t     err;
23    ngx_event_t  *wev;
24    ngx_array_t   vec;
25    ngx_chain_t  *cl;
26    LPWSABUF      wsabuf;
27    WSABUF        wsabufs[NGX_WSABUFS];
28
29    wev = c->write;
30
31    if (!wev->ready) {
32        return in;
33    }
34
35    /* the maximum limit size is the maximum u_long value - the page size */
36
37    if (limit == 0 || limit > (off_t) (NGX_MAX_UINT32_VALUE - ngx_pagesize)) {
38        limit = NGX_MAX_UINT32_VALUE - ngx_pagesize;
39    }
40
41    send = 0;
42
43    /*
44     * WSABUFs must be 4-byte aligned otherwise
45     * WSASend() will return undocumented WSAEINVAL error.
46     */
47
48    vec.elts = wsabufs;
49    vec.size = sizeof(WSABUF);
50    vec.nalloc = NGX_WSABUFS;
51    vec.pool = c->pool;
52
53    for ( ;; ) {
54        prev = NULL;
55        wsabuf = NULL;
56        prev_send = send;
57
58        vec.nelts = 0;
59
60        /* create the WSABUF and coalesce the neighbouring bufs */
61
62        for (cl = in;
63             cl && vec.nelts < ngx_max_wsabufs && send < limit;
64             cl = cl->next)
65        {
66            if (ngx_buf_special(cl->buf)) {
67                continue;
68            }
69
70            size = cl->buf->last - cl->buf->pos;
71
72            if (send + size > limit) {
73                size = (u_long) (limit - send);
74            }
75
76            if (prev == cl->buf->pos) {
77                wsabuf->len += cl->buf->last - cl->buf->pos;
78
79            } else {
80                wsabuf = ngx_array_push(&vec);
81                if (wsabuf == NULL) {
82                    return NGX_CHAIN_ERROR;
83                }
84
85                wsabuf->buf = (char *) cl->buf->pos;
86                wsabuf->len = cl->buf->last - cl->buf->pos;
87            }
88
89            prev = cl->buf->last;
90            send += size;
91        }
92
93        sent = 0;
94
95        rc = WSASend(c->fd, vec.elts, vec.nelts, &sent, 0, NULL, NULL);
96
97        if (rc == -1) {
98            err = ngx_errno;
99
100            if (err == WSAEWOULDBLOCK) {
101                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
102                               "WSASend() not ready");
103
104            } else {
105                wev->error = 1;
106                ngx_connection_error(c, err, "WSASend() failed");
107                return NGX_CHAIN_ERROR;
108            }
109        }
110
111        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
112                       "WSASend: fd:%d, s:%ul", c->fd, sent);
113
114        c->sent += sent;
115
116        in = ngx_chain_update_sent(in, sent);
117
118        if (send - prev_send != sent) {
119            wev->ready = 0;
120            return in;
121        }
122
123        if (send >= limit || in == NULL) {
124            return in;
125        }
126    }
127}
128
129
130ngx_chain_t *
131ngx_overlapped_wsasend_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
132{
133    int               rc;
134    u_char           *prev;
135    u_long            size, send, sent;
136    ngx_err_t         err;
137    ngx_event_t      *wev;
138    ngx_array_t       vec;
139    ngx_chain_t      *cl;
140    LPWSAOVERLAPPED   ovlp;
141    LPWSABUF          wsabuf;
142    WSABUF            wsabufs[NGX_WSABUFS];
143
144    wev = c->write;
145
146    if (!wev->ready) {
147        return in;
148    }
149
150    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
151                   "wev->complete: %d", wev->complete);
152
153    if (!wev->complete) {
154
155        /* post the overlapped WSASend() */
156
157        /* the maximum limit size is the maximum u_long value - the page size */
158
159        if (limit == 0 || limit > (off_t) (NGX_MAX_UINT32_VALUE - ngx_pagesize))
160        {
161            limit = NGX_MAX_UINT32_VALUE - ngx_pagesize;
162        }
163
164        /*
165         * WSABUFs must be 4-byte aligned otherwise
166         * WSASend() will return undocumented WSAEINVAL error.
167         */
168
169        vec.elts = wsabufs;
170        vec.nelts = 0;
171        vec.size = sizeof(WSABUF);
172        vec.nalloc = NGX_WSABUFS;
173        vec.pool = c->pool;
174
175        send = 0;
176        prev = NULL;
177        wsabuf = NULL;
178
179        /* create the WSABUF and coalesce the neighbouring bufs */
180
181        for (cl = in;
182             cl && vec.nelts < ngx_max_wsabufs && send < limit;
183             cl = cl->next)
184        {
185            if (ngx_buf_special(cl->buf)) {
186                continue;
187            }
188
189            size = cl->buf->last - cl->buf->pos;
190
191            if (send + size > limit) {
192                size = (u_long) (limit - send);
193            }
194
195            if (prev == cl->buf->pos) {
196                wsabuf->len += cl->buf->last - cl->buf->pos;
197
198            } else {
199                wsabuf = ngx_array_push(&vec);
200                if (wsabuf == NULL) {
201                    return NGX_CHAIN_ERROR;
202                }
203
204                wsabuf->buf = (char *) cl->buf->pos;
205                wsabuf->len = cl->buf->last - cl->buf->pos;
206            }
207
208            prev = cl->buf->last;
209            send += size;
210        }
211
212        ovlp = (LPWSAOVERLAPPED) &c->write->ovlp;
213        ngx_memzero(ovlp, sizeof(WSAOVERLAPPED));
214
215        rc = WSASend(c->fd, vec.elts, vec.nelts, &sent, 0, ovlp, NULL);
216
217        wev->complete = 0;
218
219        if (rc == -1) {
220            err = ngx_errno;
221
222            if (err == WSA_IO_PENDING) {
223                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
224                               "WSASend() posted");
225                wev->active = 1;
226                return in;
227
228            } else {
229                wev->error = 1;
230                ngx_connection_error(c, err, "WSASend() failed");
231                return NGX_CHAIN_ERROR;
232            }
233
234        } else if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
235
236            /*
237             * if a socket was bound with I/O completion port then
238             * GetQueuedCompletionStatus() would anyway return its status
239             * despite that WSASend() was already complete
240             */
241
242            wev->active = 1;
243            return in;
244        }
245
246        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
247                       "WSASend: fd:%d, s:%ul", c->fd, sent);
248
249    } else {
250
251        /* the overlapped WSASend() complete */
252
253        wev->complete = 0;
254        wev->active = 0;
255
256        if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
257            if (wev->ovlp.error) {
258                ngx_connection_error(c, wev->ovlp.error, "WSASend() failed");
259                return NGX_CHAIN_ERROR;
260            }
261
262            sent = wev->available;
263
264        } else {
265            if (WSAGetOverlappedResult(c->fd, (LPWSAOVERLAPPED) &wev->ovlp,
266                                       &sent, 0, NULL)
267                == 0)
268            {
269                ngx_connection_error(c, ngx_socket_errno,
270                               "WSASend() or WSAGetOverlappedResult() failed");
271
272                return NGX_CHAIN_ERROR;
273            }
274        }
275    }
276
277    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
278                   "WSASend ovlp: fd:%d, s:%ul", c->fd, sent);
279
280    c->sent += sent;
281
282    in = ngx_chain_update_sent(in, sent);
283
284    if (in) {
285        wev->ready = 0;
286
287    } else {
288        wev->ready = 1;
289    }
290
291    return in;
292}
293