blob: 9dd223d52a79b591f7b50731a92fe11f7084b4ff [file] [log] [blame]
David 'Digit' Turner6d448802010-11-18 16:14:03 +01001/*
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"
David 'Digit' Turneraf81d742014-02-03 17:11:18 +010017#include "android/utils/eintr_wrapper.h"
David 'Digit' Turner6d448802010-11-18 16:14:03 +010018#include "unistd.h"
19
20void
21asyncReader_init(AsyncReader* ar,
22 void* buffer,
23 size_t buffsize,
24 LoopIo* io)
25{
26 ar->buffer = buffer;
27 ar->buffsize = buffsize;
28 ar->pos = 0;
Vladimir Chtchetkinedb611d52011-11-01 17:35:07 -070029 ar->io = io;
David 'Digit' Turner6d448802010-11-18 16:14:03 +010030 if (buffsize > 0)
31 loopIo_wantRead(io);
32}
33
34AsyncStatus
David 'Digit' Turnerf9e333a2011-03-17 14:57:51 +010035asyncReader_read(AsyncReader* ar)
David 'Digit' Turner6d448802010-11-18 16:14:03 +010036{
37 int ret;
38
39 if (ar->pos >= ar->buffsize) {
40 return ASYNC_COMPLETE;
41 }
42
43 do {
David 'Digit' Turneraf81d742014-02-03 17:11:18 +010044 ret = HANDLE_EINTR(
45 socket_recv(ar->io->fd,
46 ar->buffer + ar->pos,
47 ar->buffsize - ar->pos));
David 'Digit' Turner6d448802010-11-18 16:14:03 +010048 if (ret == 0) {
49 /* disconnection ! */
50 errno = ECONNRESET;
51 return ASYNC_ERROR;
52 }
53 if (ret < 0) {
David 'Digit' Turner6d448802010-11-18 16:14:03 +010054 if (errno == EWOULDBLOCK || errno == EAGAIN) {
David 'Digit' Turnerf9e333a2011-03-17 14:57:51 +010055 loopIo_wantRead(ar->io);
David 'Digit' Turner6d448802010-11-18 16:14:03 +010056 return ASYNC_NEED_MORE;
57 }
58 return ASYNC_ERROR;
59 }
60 ar->pos += ret;
61
62 } while (ar->pos < ar->buffsize);
63
David 'Digit' Turnerf9e333a2011-03-17 14:57:51 +010064 loopIo_dontWantRead(ar->io);
David 'Digit' Turner6d448802010-11-18 16:14:03 +010065 return ASYNC_COMPLETE;
66}
67
68void
69asyncWriter_init(AsyncWriter* aw,
70 const void* buffer,
71 size_t buffsize,
72 LoopIo* io)
73{
74 aw->buffer = buffer;
75 aw->buffsize = buffsize;
76 aw->pos = 0;
Vladimir Chtchetkinedb611d52011-11-01 17:35:07 -070077 aw->io = io;
David 'Digit' Turner6d448802010-11-18 16:14:03 +010078 if (buffsize > 0)
79 loopIo_wantWrite(io);
80}
81
82AsyncStatus
David 'Digit' Turnerf9e333a2011-03-17 14:57:51 +010083asyncWriter_write(AsyncWriter* aw)
David 'Digit' Turner6d448802010-11-18 16:14:03 +010084{
85 int ret;
86
87 if (aw->pos >= aw->buffsize) {
88 return ASYNC_COMPLETE;
89 }
90
91 do {
David 'Digit' Turneraf81d742014-02-03 17:11:18 +010092 ret = HANDLE_EINTR(
93 socket_send(aw->io->fd,
94 aw->buffer + aw->pos,
95 aw->buffsize - aw->pos));
David 'Digit' Turner6d448802010-11-18 16:14:03 +010096 if (ret == 0) {
97 /* disconnection ! */
98 errno = ECONNRESET;
99 return ASYNC_ERROR;
100 }
101 if (ret < 0) {
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100102 if (errno == EWOULDBLOCK || errno == EAGAIN) {
103 return ASYNC_NEED_MORE;
104 }
105 return ASYNC_ERROR;
106 }
107 aw->pos += ret;
108
109 } while (aw->pos < aw->buffsize);
110
David 'Digit' Turnerf9e333a2011-03-17 14:57:51 +0100111 loopIo_dontWantWrite(aw->io);
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100112 return ASYNC_COMPLETE;
113}
114
115
116void
117asyncLineReader_init(AsyncLineReader* alr,
118 void* buffer,
119 size_t buffsize,
120 LoopIo* io)
121{
122 alr->buffer = buffer;
123 alr->buffsize = buffsize;
124 alr->pos = 0;
David 'Digit' Turnerf9e333a2011-03-17 14:57:51 +0100125 alr->io = io;
Vladimir Chtchetkinedb611d52011-11-01 17:35:07 -0700126 alr->eol = '\n';
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100127 if (buffsize > 0)
128 loopIo_wantRead(io);
129}
130
131AsyncStatus
David 'Digit' Turnerf9e333a2011-03-17 14:57:51 +0100132asyncLineReader_read(AsyncLineReader* alr)
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100133{
134 int ret;
135
136 if (alr->pos >= alr->buffsize) {
137 errno = ENOMEM;
138 return ASYNC_ERROR;
139 }
140
141 do {
142 char ch;
David 'Digit' Turneraf81d742014-02-03 17:11:18 +0100143 ret = HANDLE_EINTR(socket_recv(alr->io->fd, &ch, 1));
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100144 if (ret == 0) {
145 /* disconnection ! */
146 errno = ECONNRESET;
147 return ASYNC_ERROR;
148 }
149 if (ret < 0) {
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100150 if (errno == EWOULDBLOCK || errno == EAGAIN) {
David 'Digit' Turnerf9e333a2011-03-17 14:57:51 +0100151 loopIo_wantRead(alr->io);
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100152 return ASYNC_NEED_MORE;
153 }
154 return ASYNC_ERROR;
155 }
156 alr->buffer[alr->pos++] = (uint8_t)ch;
Vladimir Chtchetkinedb611d52011-11-01 17:35:07 -0700157 if (ch == alr->eol) {
David 'Digit' Turnerf9e333a2011-03-17 14:57:51 +0100158 loopIo_dontWantRead(alr->io);
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100159 return ASYNC_COMPLETE;
160 }
161 } while (alr->pos < alr->buffsize);
162
163 /* Not enough room in the input buffer!*/
David 'Digit' Turnerf9e333a2011-03-17 14:57:51 +0100164 loopIo_dontWantRead(alr->io);
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100165 errno = ENOMEM;
166 return ASYNC_ERROR;
167}
168
169const char*
170asyncLineReader_getLineRaw(AsyncLineReader* alr, int *pLength)
171{
172 if (alr->pos == 0 || alr->pos > alr->buffsize)
173 return NULL;
174
175 if (pLength != 0)
176 *pLength = alr->pos;
177
178 return (const char*) alr->buffer;
179}
180
181const char*
182asyncLineReader_getLine(AsyncLineReader* alr)
183{
184 /* Strip trailing \n if any */
185 size_t pos = alr->pos;
186 char* buffer = (char*) alr->buffer;
187
188 if (pos == 0 || pos > alr->buffsize)
189 return NULL;
190
191 pos--;
192
193 /* Check that we have a proper terminator, and replace it with 0 */
Vladimir Chtchetkinedb611d52011-11-01 17:35:07 -0700194 if (alr->eol == '\n') {
195 if (buffer[pos] != '\n')
196 return NULL;
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100197
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100198 buffer[pos] = '\0';
Vladimir Chtchetkinedb611d52011-11-01 17:35:07 -0700199
200 /* Also strip \r\n */
201 if (pos > 0 && buffer[--pos] == '\r') {
202 buffer[pos] = '\0';
203 }
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100204 }
205
206 return (const char*) buffer;
207}
208
209
210enum {
211 CONNECT_ERROR = 0,
212 CONNECT_CONNECTING,
213 CONNECT_COMPLETED
214};
215
216AsyncStatus
217asyncConnector_init(AsyncConnector* ac,
218 const SockAddress* address,
219 LoopIo* io)
220{
221 int ret;
222 ac->error = 0;
David 'Digit' Turnerf9e333a2011-03-17 14:57:51 +0100223 ac->io = io;
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100224 ret = socket_connect(io->fd, address);
225 if (ret == 0) {
226 ac->state = CONNECT_COMPLETED;
227 return ASYNC_COMPLETE;
228 }
229 if (errno == EINPROGRESS || errno == EWOULDBLOCK || errno == EAGAIN) {
230 ac->state = CONNECT_CONNECTING;
231 /* The socket will be marked writable for select() when the
232 * connection is established, or when it is definitely
233 * refused / timed-out, for any reason. */
234 loopIo_wantWrite(io);
235 return ASYNC_NEED_MORE;
236 }
237 ac->error = errno;
238 ac->state = CONNECT_ERROR;
239 return ASYNC_ERROR;
240}
241
242AsyncStatus
David 'Digit' Turnerf9e333a2011-03-17 14:57:51 +0100243asyncConnector_run(AsyncConnector* ac)
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100244{
245 switch (ac->state) {
246 case CONNECT_ERROR:
247 errno = ac->error;
248 return ASYNC_ERROR;
249
250 case CONNECT_CONNECTING:
David 'Digit' Turnerf9e333a2011-03-17 14:57:51 +0100251 loopIo_dontWantWrite(ac->io);
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100252 /* We need to read the socket error to determine if
253 * the connection was really succesful or not. This
254 * is optional, because in case of error a future
Vladimir Chtchetkine17ecca62011-02-07 14:14:14 -0800255 * socket_recv() or socket_send() will fail anyway, but this
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100256 * allows us to get a better error value as soon as
257 * possible.
258 */
David 'Digit' Turnerf9e333a2011-03-17 14:57:51 +0100259 ac->error = socket_get_error(ac->io->fd);
David 'Digit' Turner6d448802010-11-18 16:14:03 +0100260 if (ac->error == 0) {
261 ac->state = CONNECT_COMPLETED;
262 return ASYNC_COMPLETE;
263 }
264 ac->state = CONNECT_ERROR;
265 errno = ac->error;
266 return ASYNC_ERROR;
267
268 default:
269 return ASYNC_COMPLETE;
270 }
271}
Vladimir Chtchetkineef4ccd32012-04-03 10:27:12 -0700272
273int
274asyncConnector_stop(AsyncConnector* ac)
275{
276 if (ac->state == CONNECT_CONNECTING) {
277 loopIo_dontWantWrite(ac->io);
278 ac->state = CONNECT_COMPLETED;
279 return 0;
280 }
281 return -1;
282}