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