static void tcp_write_stream(OutputStream * out, int byte) { ChannelTCP * c = channel2tcp(out2channel(out)); assert(c->magic == CHANNEL_MAGIC); if (!c->chan.out.supports_zero_copy || c->chan.out.cur >= c->chan.out.end - 32 || byte < 0) { if (c->out_bin_block != NULL) tcp_bin_block_end(c); if (c->chan.out.cur == c->chan.out.end) tcp_flush_with_flags(c, MSG_MORE); if (byte < 0 || byte == ESC) { char esc = 0; *c->chan.out.cur++ = ESC; if (byte == ESC) esc = 0; else if (byte == MARKER_EOM) esc = 1; else if (byte == MARKER_EOS) esc = 2; else assert(0); if (c->chan.out.cur == c->chan.out.end) tcp_flush_with_flags(c, MSG_MORE); *c->chan.out.cur++ = esc; if (byte == MARKER_EOM && c->out_flush_cnt < 4) { if (c->out_flush_cnt++ == 0) tcp_lock(&c->chan); post_event(tcp_flush_event, c); } return; } } else if (c->out_bin_block == NULL) { tcp_bin_block_start(c); } *c->chan.out.cur++ = (char)byte; }
static int tcp_is_closed(Channel * channel) { ChannelTCP * c = channel2tcp(channel); assert(is_dispatch_thread()); assert(c->magic == CHANNEL_MAGIC); assert(c->lock_cnt > 0); return c->socket < 0; }
static int tcp_is_closed(Channel * channel) { ChannelTCP * c = channel2tcp(channel); assert(is_dispatch_thread()); assert(c->magic == CHANNEL_MAGIC); assert(c->lock_cnt > 0); return c->chan.state == ChannelStateDisconnected; }
static void tcp_flush_with_flags(OutputStream * out, int flags) { int cnt = 0; ChannelTCP * c = channel2tcp(out2channel(out)); assert(is_dispatch_thread()); assert(c->magic == CHANNEL_MAGIC); assert(c->obuf_inp <= BUF_SIZE); if (c->obuf_inp == 0) return; if (c->socket < 0 || c->out_errno) { c->obuf_inp = 0; return; } while (cnt < c->obuf_inp) { int wr = 0; if (c->ssl) { #if ENABLE_SSL wr = SSL_write(c->ssl, c->obuf + cnt, c->obuf_inp - cnt); if (wr <= 0) { int err = SSL_get_error(c->ssl, wr); if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { struct timeval tv; fd_set readfds; fd_set writefds; fd_set errorfds; FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&errorfds); if (err == SSL_ERROR_WANT_READ) FD_SET(c->socket, &readfds); if (err == SSL_ERROR_WANT_WRITE) FD_SET(c->socket, &writefds); FD_SET(c->socket, &errorfds); tv.tv_sec = 10L; tv.tv_usec = 0; if (select(c->socket + 1, &readfds, &writefds, &errorfds, &tv) >= 0) continue; } trace(LOG_PROTOCOL, "Can't SSL_write() on channel %#lx: %s", c, ERR_error_string(ERR_get_error(), NULL)); c->out_errno = EIO; c->obuf_inp = 0; return; } #else assert(0); #endif } else { wr = send(c->socket, c->obuf + cnt, c->obuf_inp - cnt, flags); if (wr < 0) { int err = errno; trace(LOG_PROTOCOL, "Can't send() on channel %#lx: %d %s", c, err, errno_to_str(err)); c->out_errno = err; c->obuf_inp = 0; return; } } cnt += wr; } assert(cnt == c->obuf_inp); c->obuf_inp = 0; }
static void tcp_unlock(Channel * channel) { ChannelTCP * c = channel2tcp(channel); assert(is_dispatch_thread()); assert(c->magic == CHANNEL_MAGIC); assert(c->lock_cnt > 0); c->lock_cnt--; if (c->lock_cnt == 0 && !c->read_pending) { delete_channel(c); } }
static ssize_t tcp_splice_block_stream(OutputStream * out, int fd, size_t size, int64_t * offset) { assert(is_dispatch_thread()); if (size == 0) return 0; #if ENABLE_Splice { ChannelTCP * c = channel2tcp(out2channel(out)); if (!c->ssl && out->supports_zero_copy) { ssize_t rd = splice(fd, offset, c->pipefd[1], NULL, size, SPLICE_F_MOVE); if (rd > 0) { /* Send the binary data escape seq */ size_t n = rd; if (c->out_bin_block != NULL) tcp_bin_block_end(c); if (c->chan.out.cur >= c->chan.out.end - 8) tcp_flush_with_flags(c, MSG_MORE); *c->chan.out.cur++ = ESC; *c->chan.out.cur++ = 3; for (;;) { if (n <= 0x7fu) { *c->chan.out.cur++ = (char)n; break; } *c->chan.out.cur++ = (n & 0x7fu) | 0x80u; n = n >> 7; } /* We need to flush the buffer then send our data */ tcp_flush_with_flags(c, MSG_MORE); #if ENABLE_OutputQueue while (!output_queue_is_empty(&c->out_queue)) { cancel_event(done_write_request, &c->wr_req, 1); done_write_request(&c->wr_req); } #endif if (c->chan.state == ChannelStateDisconnected) return rd; if (c->out_errno) return rd; n = rd; while (n > 0) { ssize_t wr = splice(c->pipefd[0], NULL, c->socket, NULL, n, SPLICE_F_MORE); if (wr < 0) { c->out_errno = errno; trace(LOG_PROTOCOL, "Error in socket splice: %s", errno_to_str(errno)); break; } n -= wr; } } return rd; }
static void tcp_write_block_stream(OutputStream * out, const char * bytes, size_t size) { size_t cnt = 0; ChannelTCP * c = channel2tcp(out2channel(out)); #if ENABLE_ZeroCopy if (!c->ssl && out->supports_zero_copy && size > 32) { /* Send the binary data escape seq */ size_t n = size; if (c->obuf_inp >= BUF_SIZE - 8) tcp_flush_with_flags(out, MSG_MORE); c->obuf[c->obuf_inp++] = ESC; c->obuf[c->obuf_inp++] = 3; for (;;) { if (n <= 0x7fu) { c->obuf[c->obuf_inp++] = (char)n; break; } c->obuf[c->obuf_inp++] = (n & 0x7fu) | 0x80u; n = n >> 7; } /* We need to flush the buffer then send our data */ tcp_flush_with_flags(out, MSG_MORE); if (c->socket < 0) return; if (c->out_errno) return; while (cnt < size) { int wr = send(c->socket, bytes + cnt, size - cnt, MSG_MORE); if (wr < 0) { int err = errno; trace(LOG_PROTOCOL, "Can't send() on channel %#lx: %d %s", c, err, errno_to_str(err)); c->out_errno = err; return; } cnt += wr; } return; }
static void tcp_write_stream(OutputStream * out, int byte) { ChannelTCP * c = channel2tcp(out2channel(out)); assert(is_dispatch_thread()); assert(c->magic == CHANNEL_MAGIC); if (c->socket < 0) return; if (c->out_errno) return; if (c->obuf_inp == BUF_SIZE) tcp_flush_with_flags(out, MSG_MORE); c->obuf[c->obuf_inp++] = (char)(byte < 0 ? ESC : byte); if (byte < 0 || byte == ESC) { char esc = 0; if (byte == ESC) esc = 0; else if (byte == MARKER_EOM) esc = 1; else if (byte == MARKER_EOS) esc = 2; else assert(0); if (c->socket < 0) return; if (c->out_errno) return; if (c->obuf_inp == BUF_SIZE) tcp_flush_with_flags(out, MSG_MORE); c->obuf[c->obuf_inp++] = esc; } if (byte == MARKER_EOM) { int congestion_level = out2channel(out)->congestion_level; if (congestion_level > 0) usleep(congestion_level * 2500); } }
static void tcp_lock(Channel * channel) { ChannelTCP * c = channel2tcp(channel); assert(is_dispatch_thread()); assert(c->magic == CHANNEL_MAGIC); c->lock_cnt++; }