size_t api_stream_on_write(struct api_filter_t* filter, const char* buffer, size_t length) { api_stream_t* stream = filter->stream; api_stream_write_t write; api_timer_t timeout; struct timespec start, end, elapsed; uint64_t timeout_value = stream->write_timeout; if (length == 0) return length; if (stream->status.write_timeout || stream->status.error != API__OK || stream->status.closed || stream->status.peer_closed || stream->status.terminated) return -1; if (stream->loop->base.terminated) return -1; write.buffer = buffer; write.length = length; write.offset = 0; write.task = stream->loop->base.scheduler.current; stream->os_linux.reserved[1] = &write; if (timeout_value > 0) { memset(&timeout, 0, sizeof(timeout)); timeout.task = write.task; api_timeout_exec(&stream->loop->base.timeouts, &timeout, timeout_value); } clock_gettime(CLOCK_MONOTONIC, &start); api_loop_write_add(stream->loop, stream->fd, &stream->os_linux.e); do { api_task_sleep(write.task); if (stream->status.write_timeout || stream->status.error != API__OK || stream->status.closed || stream->status.peer_closed || stream->status.terminated) break; if (timeout_value > 0 && timeout.elapsed) break; } while (write.offset < write.length); api_loop_write_del(stream->loop, stream->fd, &stream->os_linux.e); clock_gettime(CLOCK_MONOTONIC, &end); if (end.tv_nsec - start.tv_nsec < 0) { elapsed.tv_sec = end.tv_sec - start.tv_sec - 1; elapsed.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; } else { elapsed.tv_sec = end.tv_sec - start.tv_sec; elapsed.tv_nsec = end.tv_nsec - start.tv_nsec; } if (timeout_value > 0) api_timeout_exec(&stream->loop->base.timeouts, &timeout, 0); stream->os_linux.reserved[1] = 0; stream->write_bandwidth.sent += write.offset; stream->write_bandwidth.period += elapsed.tv_sec * 1000000 + elapsed.tv_nsec / 1000; if (timeout_value > 0 && timeout.elapsed) { stream->status.write_timeout = 1; stream->filter_head->on_write_timeout(stream->filter_head); } return write.offset; }
int api_tcp_connect(api_tcp_t* tcp, api_loop_t* loop, const char* ip, int port, uint64_t tmeout) { struct sockaddr_in* addr_in = (struct sockaddr_in*)&tcp->address.address; struct sockaddr_in6* addr_in6 = (struct sockaddr_in6*)&tcp->address.address; struct sockaddr* a = (struct sockaddr*)&tcp->address.address; int error = API__OK; api_timer_t timeout; uint64_t timeout_value = tmeout; memset(tcp, 0, sizeof(*tcp)); if (loop->terminated) { tcp->stream.status.terminated = 1; return API__TERMINATE; } tcp->stream.os_linux.processor = api_tcp_connect_processor; tcp->stream.os_linux.reserved[0] = loop->scheduler.current; tcp->stream.os_linux.e.data.ptr = &tcp->stream.os_linux; if (strchr(ip, ':') == 0) { // ipv4 tcp->stream.fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP); tcp->address.length = sizeof(struct sockaddr_in); addr_in->sin_family = AF_INET; addr_in->sin_addr.s_addr = inet_addr(ip); addr_in->sin_port = htons(port); } else { // ipv6 tcp->stream.fd = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP); tcp->address.length = sizeof(struct sockaddr_in6); addr_in6->sin6_family = AF_INET6; addr_in6->sin6_addr = in6addr_any; addr_in6->sin6_port = htons(port); inet_pton(AF_INET6, ip, (void*)&addr_in6->sin6_addr.__in6_u); } error = api_socket_non_block(tcp->stream.fd, 1); error = api_socket_recv_buffer_size(tcp->stream.fd, 0); error = api_socket_send_buffer_size(tcp->stream.fd, 0); error = api_tcp_nodelay(tcp->stream.fd, 1); if (0 != connect(tcp->stream.fd, a, tcp->address.length)) { if (errno != EINPROGRESS) { error = api_error_translate(errno); } else { // wait needed tcp->stream.os_linux.e.events = EPOLLERR | EPOLLHUP | EPOLLRDHUP | EPOLLOUT; error = epoll_ctl(loop->epoll, EPOLL_CTL_ADD, tcp->stream.fd, &tcp->stream.os_linux.e); error = api_error_translate(error); if (API__OK == error) { error = api_loop_write_add(loop, tcp->stream.fd, &tcp->stream.os_linux.e); if (API__OK != error) { epoll_ctl(loop->epoll, EPOLL_CTL_DEL, tcp->stream.fd, &tcp->stream.os_linux.e); } else { if (timeout_value > 0) { memset(&timeout, 0, sizeof(timeout)); timeout.task = loop->scheduler.current; api_timeout_exec(&loop->timeouts, &timeout, timeout_value); } api_task_sleep(loop->scheduler.current); if (timeout_value > 0) api_timeout_exec(&loop->timeouts, &timeout, 0); if (timeout_value > 0 && timeout.elapsed) { tcp->stream.status.read_timeout = 1; error = api_error_translate(ETIMEDOUT); } if (tcp->stream.status.closed || tcp->stream.status.terminated || tcp->stream.status.error != API__OK) { epoll_ctl(loop->epoll, EPOLL_CTL_DEL, tcp->stream.fd, &tcp->stream.os_linux.e); } else { api_loop_write_del(loop, tcp->stream.fd, &tcp->stream.os_linux.e); } } } } } else { // completed immediately } if (API__OK == error && tcp->stream.status.closed == 0 && tcp->stream.status.terminated == 0 && tcp->stream.status.error == API__OK) { api_stream_init(&tcp->stream, STREAM_Tcp, tcp->stream.fd); tcp->stream.loop = loop; api_loop_ref(loop); return API__OK; } close(tcp->stream.fd); if (API__OK != error) return error; if (API__OK != tcp->stream.status.error) return tcp->stream.status.error; return -1; }