1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include "android/async-utils.h"
17#include "unistd.h"
18
19void
20asyncReader_init(AsyncReader* ar,
21                 void*        buffer,
22                 size_t       buffsize,
23                 LoopIo*      io)
24{
25    ar->buffer   = buffer;
26    ar->buffsize = buffsize;
27    ar->pos      = 0;
28    ar->io       = io;
29    if (buffsize > 0)
30        loopIo_wantRead(io);
31}
32
33AsyncStatus
34asyncReader_read(AsyncReader*  ar)
35{
36    int  ret;
37
38    if (ar->pos >= ar->buffsize) {
39        return ASYNC_COMPLETE;
40    }
41
42    do {
43        ret = socket_recv(ar->io->fd, ar->buffer + ar->pos, ar->buffsize - ar->pos);
44        if (ret == 0) {
45            /* disconnection ! */
46            errno = ECONNRESET;
47            return ASYNC_ERROR;
48        }
49        if (ret < 0) {
50            if (errno == EINTR) /* loop on EINTR */
51                continue;
52            if (errno == EWOULDBLOCK || errno == EAGAIN) {
53                loopIo_wantRead(ar->io);
54                return ASYNC_NEED_MORE;
55            }
56            return ASYNC_ERROR;
57        }
58        ar->pos += ret;
59
60    } while (ar->pos < ar->buffsize);
61
62    loopIo_dontWantRead(ar->io);
63    return ASYNC_COMPLETE;
64}
65
66void
67asyncWriter_init(AsyncWriter*  aw,
68                 const void*   buffer,
69                 size_t        buffsize,
70                 LoopIo*       io)
71{
72    aw->buffer   = buffer;
73    aw->buffsize = buffsize;
74    aw->pos      = 0;
75    aw->io       = io;
76    if (buffsize > 0)
77        loopIo_wantWrite(io);
78}
79
80AsyncStatus
81asyncWriter_write(AsyncWriter* aw)
82{
83    int  ret;
84
85    if (aw->pos >= aw->buffsize) {
86        return ASYNC_COMPLETE;
87    }
88
89    do {
90        ret = socket_send(aw->io->fd, aw->buffer + aw->pos, aw->buffsize - aw->pos);
91        if (ret == 0) {
92            /* disconnection ! */
93            errno = ECONNRESET;
94            return ASYNC_ERROR;
95        }
96        if (ret < 0) {
97            if (errno == EINTR) /* loop on EINTR */
98                continue;
99            if (errno == EWOULDBLOCK || errno == EAGAIN) {
100                return ASYNC_NEED_MORE;
101            }
102            return ASYNC_ERROR;
103        }
104        aw->pos += ret;
105
106    } while (aw->pos < aw->buffsize);
107
108    loopIo_dontWantWrite(aw->io);
109    return ASYNC_COMPLETE;
110}
111
112
113void
114asyncLineReader_init(AsyncLineReader* alr,
115                     void*            buffer,
116                     size_t           buffsize,
117                     LoopIo*          io)
118{
119    alr->buffer   = buffer;
120    alr->buffsize = buffsize;
121    alr->pos      = 0;
122    alr->io       = io;
123    alr->eol      = '\n';
124    if (buffsize > 0)
125        loopIo_wantRead(io);
126}
127
128AsyncStatus
129asyncLineReader_read(AsyncLineReader* alr)
130{
131    int  ret;
132
133    if (alr->pos >= alr->buffsize) {
134        errno = ENOMEM;
135        return ASYNC_ERROR;
136    }
137
138    do {
139        char ch;
140        ret = socket_recv(alr->io->fd, &ch, 1);
141        if (ret == 0) {
142            /* disconnection ! */
143            errno = ECONNRESET;
144            return ASYNC_ERROR;
145        }
146        if (ret < 0) {
147            if (errno == EINTR) /* loop on EINTR */
148                continue;
149            if (errno == EWOULDBLOCK || errno == EAGAIN) {
150                loopIo_wantRead(alr->io);
151                return ASYNC_NEED_MORE;
152            }
153            return ASYNC_ERROR;
154        }
155        alr->buffer[alr->pos++] = (uint8_t)ch;
156        if (ch == alr->eol) {
157            loopIo_dontWantRead(alr->io);
158            return ASYNC_COMPLETE;
159        }
160    } while (alr->pos < alr->buffsize);
161
162    /* Not enough room in the input buffer!*/
163    loopIo_dontWantRead(alr->io);
164    errno = ENOMEM;
165    return ASYNC_ERROR;
166}
167
168const char*
169asyncLineReader_getLineRaw(AsyncLineReader* alr, int *pLength)
170{
171    if (alr->pos == 0 || alr->pos > alr->buffsize)
172        return NULL;
173
174    if (pLength != 0)
175        *pLength = alr->pos;
176
177    return (const char*) alr->buffer;
178}
179
180const char*
181asyncLineReader_getLine(AsyncLineReader* alr)
182{
183    /* Strip trailing \n if any */
184    size_t  pos = alr->pos;
185    char*   buffer = (char*) alr->buffer;
186
187    if (pos == 0 || pos > alr->buffsize)
188        return NULL;
189
190    pos--;
191
192    /* Check that we have a proper terminator, and replace it with 0 */
193    if (alr->eol == '\n') {
194        if (buffer[pos] != '\n')
195            return NULL;
196
197        buffer[pos] = '\0';
198
199        /* Also strip \r\n */
200        if (pos > 0 && buffer[--pos] == '\r') {
201            buffer[pos] = '\0';
202        }
203    }
204
205    return (const char*) buffer;
206}
207
208
209enum {
210    CONNECT_ERROR = 0,
211    CONNECT_CONNECTING,
212    CONNECT_COMPLETED
213};
214
215AsyncStatus
216asyncConnector_init(AsyncConnector*    ac,
217                    const SockAddress* address,
218                    LoopIo*            io)
219{
220    int ret;
221    ac->error = 0;
222    ac->io    = io;
223    ret = socket_connect(io->fd, address);
224    if (ret == 0) {
225        ac->state = CONNECT_COMPLETED;
226        return ASYNC_COMPLETE;
227    }
228    if (errno == EINPROGRESS || errno == EWOULDBLOCK || errno == EAGAIN) {
229        ac->state = CONNECT_CONNECTING;
230        /* The socket will be marked writable for select() when the
231         * connection is established, or when it is definitely
232         * refused / timed-out, for any reason. */
233        loopIo_wantWrite(io);
234        return ASYNC_NEED_MORE;
235    }
236    ac->error = errno;
237    ac->state = CONNECT_ERROR;
238    return ASYNC_ERROR;
239}
240
241AsyncStatus
242asyncConnector_run(AsyncConnector* ac)
243{
244    switch (ac->state) {
245    case CONNECT_ERROR:
246        errno = ac->error;
247        return ASYNC_ERROR;
248
249    case CONNECT_CONNECTING:
250        loopIo_dontWantWrite(ac->io);
251        /* We need to read the socket error to determine if
252            * the connection was really succesful or not. This
253            * is optional, because in case of error a future
254            * socket_recv() or socket_send() will fail anyway, but this
255            * allows us to get a better error value as soon as
256            * possible.
257            */
258        ac->error = socket_get_error(ac->io->fd);
259        if (ac->error == 0) {
260            ac->state = CONNECT_COMPLETED;
261            return ASYNC_COMPLETE;
262        }
263        ac->state = CONNECT_ERROR;
264        errno = ac->error;
265        return ASYNC_ERROR;
266
267    default:
268        return ASYNC_COMPLETE;
269    }
270}
271
272int
273asyncConnector_stop(AsyncConnector* ac)
274{
275    if (ac->state == CONNECT_CONNECTING) {
276        loopIo_dontWantWrite(ac->io);
277        ac->state = CONNECT_COMPLETED;
278        return 0;
279    }
280    return -1;
281}
282