git2go icon indicating copy to clipboard operation
git2go copied to clipboard

Libgit2 return not wanted error: libgit2 was not built with OpenSSL support

Open dbadura opened this issue 3 years ago • 1 comments

When you run following code:

func TestLibGit2InitError(t *testing.T) {
	// the more time pass, the possibility of openssl init error occurrence is smaller, because something cleans it
	time.Sleep(3 * time.Microsecond)
	//if sleep is set to less than 3 um, then we have a high possibility of noticid git openssl init error
	gitErr := git2go.MakeGitError2(int(git2go.ErrorCodeNotFound))
	fmt.Println(gitErr)
}

Git2go sometimes return correct error, sometimes return "libgit2 was not built with OpenSSL support". It depends on sleep duration.

I have got the same results with following versions:

  • libgit2-dev libgit2-1.1/now 1.1.0+dfsg.1-4 amd64 [installed,local]
  • libgit2-dev/experimental,now 1.3.0+dfsg.1-1 amd64 [installed]

I found that following code is executed: https://github.com/libgit2/libgit2/blob/main/src/streams/openssl.c#L741

dbadura avatar Jan 12 '22 14:01 dbadura

package git

import (
	"fmt"
	"testing"
	"time"

	"github.com/libgit2/git2go/v33"
)

// MakeGitError2 mimics the git2go error creation function (adjust to match your codebase).
func MakeGitError2(code int) error {
	return git2go.NewError(git2go.ErrorCode(code))
}

// initLibgit2 ensures libgit2 is initialized and OpenSSL is ready.
func initLibgit2() error {
	if err := git2go.Init(); err != nil {
		return fmt.Errorf("failed to initialize libgit2: %v", err)
	}
	return nil
}

func TestLibGit2InitError(t *testing.T) {
	// Ensure libgit2 is initialized
	if err := initLibgit2(); err != nil {
		t.Fatalf("initLibgit2 failed: %v", err)
	}
	defer git2go.Shutdown()

	// Test with different sleep durations
	for _, sleepDuration := range []time.Duration{
		1 * time.Microsecond,
		3 * time.Microsecond,
		10 * time.Microsecond,
	} {
		t.Run(fmt.Sprintf("Sleep_%v", sleepDuration), func(t *testing.T) {
			// Simulate race condition with sleep
			time.Sleep(sleepDuration)

			// Create error
			gitErr := MakeGitError2(int(git2go.ErrorCodeNotFound))
			fmt.Println(gitErr)

			// Check if the error is the expected NotFound or an OpenSSL error
			if gitErr.Error() == "libgit2 was not built with OpenSSL support" {
				t.Errorf("unexpected OpenSSL error with sleep %v", sleepDuration)
			}
		})
	}
}

// Workaround: Force OpenSSL initialization before tests
func TestWithForcedOpenSSLInit(t *testing.T) {
	// Explicitly initialize libgit2 and force OpenSSL stream registration
	if err := initLibgit2(); err != nil {
		t.Fatalf("initLibgit2 failed: %v", err)
	}

	// Force OpenSSL stream initialization (mimics git_stream_register_tls)
	// This requires a custom cgo call to ensure OpenSSL is ready
	if err := forceOpenSSLStreamInit(); err != nil {
		t.Fatalf("forceOpenSSLStreamInit failed: %v", err)
	}
	defer git2go.Shutdown()

	// Test with minimal sleep
	time.Sleep(1 * time.Microsecond)
	gitErr := MakeGitError2(int(git2go.ErrorCodeNotFound))
	fmt.Println(gitErr)

	if gitErr.Error() == "libgit2 was not built with OpenSSL support" {
		t.Error("unexpected OpenSSL error even with forced initialization")
	}
}

// forceOpenSSLStreamInit calls libgit2's OpenSSL stream initialization explicitly.
func forceOpenSSLStreamInit() error {
	// Using cgo to call git_stream_register_tls (adjust to your libgit2 version)
	// #cgo pkg-config: libgit2
	// #include <git2.h>
	// int init_openssl_streams() {
	//     return git_stream_register_tls();
	// }
	import "C"

	ret := C.init_openssl_streams()
	if ret != 0 {
		return fmt.Errorf("failed to register OpenSSL streams: %s", git2go.NewError(git2go.ErrorCode(ret)).Error())
	}
	return nil
}

ljluestc avatar May 22 '25 14:05 ljluestc