/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "android/async-utils.h" #include "unistd.h" void asyncReader_init(AsyncReader* ar, void* buffer, size_t buffsize, LoopIo* io) { ar->buffer = buffer; ar->buffsize = buffsize; ar->pos = 0; ar->io = io; if (buffsize > 0) loopIo_wantRead(io); } AsyncStatus asyncReader_read(AsyncReader* ar) { int ret; if (ar->pos >= ar->buffsize) { return ASYNC_COMPLETE; } do { ret = socket_recv(ar->io->fd, ar->buffer + ar->pos, ar->buffsize - ar->pos); if (ret == 0) { /* disconnection ! */ errno = ECONNRESET; return ASYNC_ERROR; } if (ret < 0) { if (errno == EINTR) /* loop on EINTR */ continue; if (errno == EWOULDBLOCK || errno == EAGAIN) { loopIo_wantRead(ar->io); return ASYNC_NEED_MORE; } return ASYNC_ERROR; } ar->pos += ret; } while (ar->pos < ar->buffsize); loopIo_dontWantRead(ar->io); return ASYNC_COMPLETE; } void asyncWriter_init(AsyncWriter* aw, const void* buffer, size_t buffsize, LoopIo* io) { aw->buffer = buffer; aw->buffsize = buffsize; aw->pos = 0; aw->io = io; if (buffsize > 0) loopIo_wantWrite(io); } AsyncStatus asyncWriter_write(AsyncWriter* aw) { int ret; if (aw->pos >= aw->buffsize) { return ASYNC_COMPLETE; } do { ret = socket_send(aw->io->fd, aw->buffer + aw->pos, aw->buffsize - aw->pos); if (ret == 0) { /* disconnection ! */ errno = ECONNRESET; return ASYNC_ERROR; } if (ret < 0) { if (errno == EINTR) /* loop on EINTR */ continue; if (errno == EWOULDBLOCK || errno == EAGAIN) { return ASYNC_NEED_MORE; } return ASYNC_ERROR; } aw->pos += ret; } while (aw->pos < aw->buffsize); loopIo_dontWantWrite(aw->io); return ASYNC_COMPLETE; } void asyncLineReader_init(AsyncLineReader* alr, void* buffer, size_t buffsize, LoopIo* io) { alr->buffer = buffer; alr->buffsize = buffsize; alr->pos = 0; alr->io = io; alr->eol = '\n'; if (buffsize > 0) loopIo_wantRead(io); } AsyncStatus asyncLineReader_read(AsyncLineReader* alr) { int ret; if (alr->pos >= alr->buffsize) { errno = ENOMEM; return ASYNC_ERROR; } do { char ch; ret = socket_recv(alr->io->fd, &ch, 1); if (ret == 0) { /* disconnection ! */ errno = ECONNRESET; return ASYNC_ERROR; } if (ret < 0) { if (errno == EINTR) /* loop on EINTR */ continue; if (errno == EWOULDBLOCK || errno == EAGAIN) { loopIo_wantRead(alr->io); return ASYNC_NEED_MORE; } return ASYNC_ERROR; } alr->buffer[alr->pos++] = (uint8_t)ch; if (ch == alr->eol) { loopIo_dontWantRead(alr->io); return ASYNC_COMPLETE; } } while (alr->pos < alr->buffsize); /* Not enough room in the input buffer!*/ loopIo_dontWantRead(alr->io); errno = ENOMEM; return ASYNC_ERROR; } const char* asyncLineReader_getLineRaw(AsyncLineReader* alr, int *pLength) { if (alr->pos == 0 || alr->pos > alr->buffsize) return NULL; if (pLength != 0) *pLength = alr->pos; return (const char*) alr->buffer; } const char* asyncLineReader_getLine(AsyncLineReader* alr) { /* Strip trailing \n if any */ size_t pos = alr->pos; char* buffer = (char*) alr->buffer; if (pos == 0 || pos > alr->buffsize) return NULL; pos--; /* Check that we have a proper terminator, and replace it with 0 */ if (alr->eol == '\n') { if (buffer[pos] != '\n') return NULL; buffer[pos] = '\0'; /* Also strip \r\n */ if (pos > 0 && buffer[--pos] == '\r') { buffer[pos] = '\0'; } } return (const char*) buffer; } enum { CONNECT_ERROR = 0, CONNECT_CONNECTING, CONNECT_COMPLETED }; AsyncStatus asyncConnector_init(AsyncConnector* ac, const SockAddress* address, LoopIo* io) { int ret; ac->error = 0; ac->io = io; ret = socket_connect(io->fd, address); if (ret == 0) { ac->state = CONNECT_COMPLETED; return ASYNC_COMPLETE; } if (errno == EINPROGRESS || errno == EWOULDBLOCK || errno == EAGAIN) { ac->state = CONNECT_CONNECTING; /* The socket will be marked writable for select() when the * connection is established, or when it is definitely * refused / timed-out, for any reason. */ loopIo_wantWrite(io); return ASYNC_NEED_MORE; } ac->error = errno; ac->state = CONNECT_ERROR; return ASYNC_ERROR; } AsyncStatus asyncConnector_run(AsyncConnector* ac) { switch (ac->state) { case CONNECT_ERROR: errno = ac->error; return ASYNC_ERROR; case CONNECT_CONNECTING: loopIo_dontWantWrite(ac->io); /* We need to read the socket error to determine if * the connection was really succesful or not. This * is optional, because in case of error a future * socket_recv() or socket_send() will fail anyway, but this * allows us to get a better error value as soon as * possible. */ ac->error = socket_get_error(ac->io->fd); if (ac->error == 0) { ac->state = CONNECT_COMPLETED; return ASYNC_COMPLETE; } ac->state = CONNECT_ERROR; errno = ac->error; return ASYNC_ERROR; default: return ASYNC_COMPLETE; } } int asyncConnector_stop(AsyncConnector* ac) { if (ac->state == CONNECT_CONNECTING) { loopIo_dontWantWrite(ac->io); ac->state = CONNECT_COMPLETED; return 0; } return -1; }