static void send_eof_and_close(Channel * channel, int err) {
    ChannelNP * c = channel2np(channel);

    assert(c->magic == CHANNEL_MAGIC);
    if (channel->state == ChannelStateDisconnected) return;
    ibuf_flush(&c->ibuf);
    if (c->ibuf.handling_msg == HandleMsgTriggered) {
        /* Cancel pending message handling */
        cancel_event(handle_channel_msg, c, 0);
        c->ibuf.handling_msg = HandleMsgIdle;
    }
    write_stream(&c->chan.out, MARKER_EOS);
    write_errno(&c->chan.out, err);
    write_stream(&c->chan.out, MARKER_EOM);
    np_flush_with_flags(c, 0);
#if ENABLE_OutputQueue
    if (output_queue_is_empty(&c->out_queue))
#endif
        nopoll_conn_shutdown(c->np_socket);
    c->chan.state = ChannelStateDisconnected;
    np_post_read(&c->ibuf, c->ibuf.buf, c->ibuf.buf_size);
    notify_channel_closed(channel);
    if (channel->disconnected) {
        channel->disconnected(channel);
    }
    else {
        trace(LOG_PROTOCOL, "channel %#lx disconnected", c);
        if (channel->protocol != NULL) protocol_release(channel->protocol);
    }
    channel->protocol = NULL;
}
Beispiel #2
0
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;
        }
Beispiel #3
0
static void done_write_request(void * args) {
    ChannelPIPE * c = (ChannelPIPE *)((AsyncReqInfo *)args)->client_data;
    int size = 0;
    int error = 0;

    assert(args == &c->out_req);
    if (c->out_req.u.fio.rval < 0) error = c->out_req.error;
    else size = c->out_req.u.fio.rval;
    output_queue_done(&c->out_queue, error, size);

    if (output_queue_is_empty(&c->out_queue) &&
        c->chan->state == ChannelStateDisconnected) close_output_pipe(c);

    pipe_unlock(c->chan);
}
Beispiel #4
0
static void done_write_request(void * args) {
    ChannelTCP * c = (ChannelTCP *)((AsyncReqInfo *)args)->client_data;
    int size = 0;
    int error = 0;

    assert(args == &c->wr_req);
    assert(c->socket >= 0);

    if (c->wr_req.u.sio.rval < 0) error = c->wr_req.error;
    else if (c->wr_req.type == AsyncReqSend) size = c->wr_req.u.sio.rval;
    output_queue_done(&c->out_queue, error, size);
    if (error) c->out_errno = error;
    if (output_queue_is_empty(&c->out_queue) &&
        c->chan.state == ChannelStateDisconnected) shutdown(c->socket, SHUT_WR);
    tcp_unlock(&c->chan);
}
static void done_write_request(void * args) {
    ChannelNP * c = (ChannelNP *)((AsyncReqInfo *)args)->client_data;
    int size = 0;
    int error = 0;

    assert(args == &c->wr_req);
    assert(c->socket != NOPOLL_INVALID_SOCKET);
    loc_free(((AsyncReqInfo *)args)->u.user.data);

    if (c->wr_req.u.user.rval < 0) error = c->wr_req.error;
    size = c->wr_req.u.user.rval;
    output_queue_done(&c->out_queue, error, size);
    if (error) c->out_errno = error;
    if (output_queue_is_empty(&c->out_queue) &&
        c->chan.state == ChannelStateDisconnected) nopoll_conn_shutdown(c->np_socket);
    np_unlock(&c->chan);
    trace(LOG_PROTOCOL, "done_write_request");
}
static void delete_channel(ChannelPIPE * c) {
    trace(LOG_PROTOCOL, "Deleting channel %#lx", c);
    assert(c->lock_cnt == 0);
    assert(c->out_flush_cnt == 0);
    assert(c->magic == CHANNEL_MAGIC);
    assert(c->read_pending == 0);
    assert(c->ibuf.handling_msg != HandleMsgTriggered);
    assert(output_queue_is_empty(&c->out_queue));
    output_queue_clear(&c->out_queue);
    channel_clear_broadcast_group(&c->chan);
    close_input_pipe(c);
    list_remove(&c->chan.chanlink);
    if (list_is_empty(&channel_root) && list_is_empty(&channel_server_root))
        shutdown_set_stopped(&channel_shutdown);
    c->magic = 0;
    loc_free(c->ibuf.buf);
    loc_free(c->chan.peer_name);
    loc_free(c);
}