Skip to content

Creating a non stateless Transport, issue in subtransport_action #681

@AmarOk1412

Description

@AmarOk1412

Hi!

First thing first, I'd like to say that this crate is nice and it's pretty cool to use.

Now, I think creating custom transport, like https://github.com/rust-lang/git2-rs/tree/master/git2-curl but with rpc=false will fail. I may be missing a thing, but I'm currently writing a custom transport with this crate. The transport is using a bidirectional channel with a custom & minimal git server I did.

struct WolfTransport {
    channel: Arc<Mutex<Channel<Vec<u8>, Vec<u8>>>>,
}

(...)

Transport::smart(
        remote,
        false, // Here git2-curl is true
        WolfTransport {
            channel
        },
    )

Now, just to get a trace, this is what I see and what is problematic:

Client: git-2-rs:transport.rs => subtransport_action with action = upload-pack-ls
Client: SmartSubtransport::action (with action = upload-pack-ls) Init subtransport, return Ok(Box::new(WolfSubTransport {
Client: WolfSubTransport::read()
Server: recv pkt: 0028git-upload-pack /zdshost=localhost
Server: send references capabilities
006ac29eb686c3638633bff5b852ee1ed76211882ba1 HEADside-band side-band-64k shallow no-progress include-tag
003f7300c6f39366e2c0bcaf99efc05cc45e8511a384 refs/heads/master
0046c29eb686c3638633bff5b852ee1ed76211882ba1 refs/remotes/origin/HEAD
0048c29eb686c3638633bff5b852ee1ed76211882ba1 refs/remotes/origin/master
Client: WolfSubTransport::read()
Server: send flush (0000)
Client: SmartSubtransport::action (with action = upload-pack) Init subtransport, return Ok(Box::new(WolfSubTransport {
Client: WolfSubTransport::write()
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error { code: -1, klass: 35, message: "unrecoverable internal error: \'t->rpc || t->current_stream == stream\'" }', src/main.rs:33:53

I don't know if it's clear, but the issue is located during the second Init subtransport. libgit2 will call subtransport_action with action=upload-pack and here, subtransport_action should and, because rpc=false re-use previous initialized stream to be able to send want xxxxxxxxx packets and start packfile negotiation.

This is basically what we will have in cpp with libgit2:

int
SubTransportAction(git_smart_subtransport_stream** out,
                      git_smart_subtransport* transport,
                      const char* url,
                      git_smart_service_t action)
{
    auto* sub = reinterpret_cast<SubTransport*>(transport);
    if (!sub || !sub->remote) {
        return -1;
    }

    if (action == GIT_SERVICE_UPLOADPACK_LS) {
        auto stream = std::make_unique<Stream>();
        // Init Stream cb
        sub->stream = std::move(stream);
        *out = &sub->stream->base;
        return 0;
    } else if (action == GIT_SERVICE_UPLOADPACK) {
        if (sub->stream) {
            *out = &sub->stream->base;
            return 0;
        }
        return -1;
    }
    return 0;
}

However in current implementation (

let obj = match transport.obj.action(url, action) {
) we will always generate a new stream else we will abort when the negotiation will starts (https://github.com/libgit2/libgit2/blob/27e34f9b9843f7bcc33a4ccfe3e395fe303cba63/src/transports/smart.c#L349)

So imho subtransport_action should be rewritten in order to be able to return the previous stream if necessary. I don't have a fix and don't know what would be the preferred solution (or if I missed something, I just saw that this morning at 1am).

Best regards,

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions