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 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_flush_event(void * x) { ChannelTCP * c = (ChannelTCP *)x; assert(c->magic == CHANNEL_MAGIC); if (--c->out_flush_cnt == 0) { int congestion_level = c->chan.congestion_level; if (congestion_level > 0) usleep(congestion_level * 2500); tcp_flush_with_flags(c, 0); tcp_unlock(&c->chan); } }
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_flush_stream(OutputStream * out) { tcp_flush_with_flags(out, 0); }