githttp

package module
v1.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 6, 2021 License: BSD-3-Clause Imports: 28 Imported by: 5

README

githttp

Documentation Go Report Card

A Go implementation of Git's HTTP "smart" protocol.

Minimalistic example with git bare repositories (with the .git extension) in the git_repositories/ directory:

package main

import (
        "github.com/inconshreveable/log15"
        "github.com/omegaup/githttp"
        "net/http"
)

func main() {
        panic(http.Server{
                Addr:    ":80",
                Handler: githttp.GitServer("git_repositories", true, nil, nil, nil, log15.New()),
        }.ListenAndServe())
}

Documentation

Index

Constants

View Source
const (
	// BlobDisplayMaxSize is the maximum size that a blob can be in order to
	// display it.
	BlobDisplayMaxSize = 1 * 1024 * 1024
)

Variables

View Source
var (
	// EmptyPackfile is a packfile that has no objects.
	EmptyPackfile = []byte{
		0x50, 0x41, 0x43, 0x4B,
		0x00, 0x00, 0x00, 0x02,
		0x00, 0x00, 0x00, 0x00,
		0x02, 0x9D, 0x08, 0x82, 0x3B, 0xD8, 0xA8, 0xEA, 0xB5, 0x10, 0xAD, 0x6A,
		0xC7, 0x5C, 0x82, 0x3C, 0xFD, 0x3E, 0xD3, 0x1E,
	}

	// ErrInvalidMagic is returned when the index file does not start with the
	// magic header.
	ErrInvalidMagic = stderrors.New("bad pack header")

	// ErrInvalidVersion is returned when the index file does not have the
	// expected version (2).
	ErrInvalidVersion = stderrors.New("bad pack version")

	// ErrLargePackfile is returned when an offset in a packfile would overflow a
	// 32-bit signed integer.
	ErrLargePackfile = stderrors.New("packfile too large")
)
View Source
var (
	// ErrBadRequest is returned when the client sends a bad request. HTTP 400
	// will be returned to http clients.
	ErrBadRequest = stderrors.New("bad-request")

	// ErrForbidden is returned if an operation is not allowed. HTTP 403 will be
	// returned to http clients.
	ErrForbidden = stderrors.New("forbidden")

	// ErrNotFound is returned if a reference is not found. HTTP 404 will be
	// returned to http clients.
	ErrNotFound = stderrors.New("not-found")

	// ErrDeleteDisallowed is returned when a delete operation is attempted.
	ErrDeleteDisallowed = stderrors.New("delete-disallowed")

	// ErrInvalidRef is returned if a reference that the system does not support
	// is attempted to be modified.
	ErrInvalidRef = stderrors.New("invalid-ref")

	// ErrReadOnlyRef is returned if a read-only reference is attempted to be
	// modified.
	ErrReadOnlyRef = stderrors.New("read-only")

	// ErrRestrictedRef is returned if a restricted reference is attempted to be
	// modified.
	ErrRestrictedRef = stderrors.New("restricted-ref")

	// ErrDeleteUnallowed is returned if a reference is attempted to be deleted.
	ErrDeleteUnallowed = stderrors.New("delete-unallowed")

	// ErrUnknownCommit is returned if the user is attempting to update a ref
	// with an unknown commit.
	ErrUnknownCommit = stderrors.New("unknown-commit")

	// ErrNonFastForward is returned if the user is attempting to update a ref
	// with a commit that is not a direct descendant of the current tip.
	ErrNonFastForward = stderrors.New("non-fast-forward")

	// ErrStaleInfo is returned if the provided old oid does not match the current tip.
	ErrStaleInfo = stderrors.New("stale-info")

	// ErrInvalidOldOid is returned if the provided old oid is not a valid object id.
	ErrInvalidOldOid = stderrors.New("invalid-old-oid")

	// ErrInvalidNewOid is returned if the provided new oid is not a valid object id.
	ErrInvalidNewOid = stderrors.New("invalid-new-oid")
)
View Source
var (
	// ErrFlush is returned whtn the client sends an explicit flush packet.
	ErrFlush = errors.New("flush")
)
View Source
var (
	// ErrObjectLimitExceeded is the error that's returned when a git tree has
	// more objects than ObjectLimit.
	ErrObjectLimitExceeded = stderrors.New("tree exceeded object limit")
)

Functions

func BuildTree

func BuildTree(
	repository *git.Repository,
	files map[string]io.Reader,
	log log15.Logger,
) (*git.Tree, error)

BuildTree recursively builds a tree based on a static map of paths and file contents.

func GitServer

func GitServer(
	rootPath string,
	repositorySuffix string,
	enableBrowse bool,
	protocol *GitProtocol,
	contextCallback ContextCallback,
	log log15.Logger,
) http.Handler

GitServer returns an http.Handler that implements git's smart protocol, as documented on https://git-scm.com/book/en/v2/Git-Internals-Transfer-Protocols#_the_smart_protocol . The callbacks will be invoked as a way to allow callers to perform additional authorization and pre-upload checks.

func MergeTrees

func MergeTrees(
	repository *git.Repository,
	trees ...*git.Tree,
) (*git.Tree, error)

MergeTrees recursively merges a set of trees. If there are any conflicts in files, the resolution is to take the contents of the file in the first tree provided. If there are any conflicts in object types (i.e. a path is a tree in one tree and a blob in another), the operation fails.

func SplitTree

func SplitTree(
	originalTree *git.Tree,
	originalRepository *git.Repository,
	paths []string,
	repository *git.Repository,
	log log15.Logger,
) (*git.Tree, error)

SplitTree extracts a tree from another, potentially in a different repository. It recursively copies all the entries given in paths.

func ValidateFastForward

func ValidateFastForward(
	repository *git.Repository,
	commit *git.Commit,
	ref *git.Reference,
) bool

ValidateFastForward returns whether there is a chain of left parent commits that lead to: * The target of the reference (if it exists). * The commit pointed to by HEAD (if it is an unborn branch, and it exists). * An unborn branch if there is no HEAD.

func WriteHeader

func WriteHeader(w http.ResponseWriter, err error, clearHeaders bool) error

WriteHeader sets the HTTP status code and optionally clears any pending headers from the reply. It also returns the cause of the HTTP error.

Types

type AuthorizationCallback

type AuthorizationCallback func(
	ctx context.Context,
	w http.ResponseWriter,
	r *http.Request,
	repositoryName string,
	operation GitOperation,
) (AuthorizationLevel, string)

AuthorizationCallback is invoked by GitServer when a user requests to perform an action. It returns the authorization level and the username that is requesting the action.

type AuthorizationLevel

type AuthorizationLevel int

AuthorizationLevel describes the result of an authorization attempt.

const (
	//AuthorizationDenied denotes that the operation was not allowed.
	AuthorizationDenied AuthorizationLevel = iota

	// AuthorizationAllowed denotes that the operation was allowed.
	AuthorizationAllowed

	// AuthorizationAllowedRestricted denotes that the operation was allowed
	// (with restrictions).
	AuthorizationAllowedRestricted

	// AuthorizationAllowedReadOnly denotes that the operation was allowed in a
	// read-only fashion.
	AuthorizationAllowedReadOnly
)

type BlobResult

type BlobResult struct {
	ID       string `json:"id"`
	Size     int64  `json:"size"`
	Contents string `json:"contents,omitempty"`
}

A BlobResult represents a git blob.

func (*BlobResult) String

func (r *BlobResult) String() string

type Capabilities

type Capabilities []string

A Capabilities represents a set of git protocol capabilities.

func (*Capabilities) Contains

func (c *Capabilities) Contains(capability string) bool

Contains returns true if the provided capability name is contained within the Capabilities set.

func (*Capabilities) Equal

func (c *Capabilities) Equal(other Capabilities) bool

Equal returns true if both capability sets are equal.

type CommitResult

type CommitResult struct {
	Commit    string           `json:"commit"`
	Tree      string           `json:"tree"`
	Parents   []string         `json:"parents"`
	Author    *SignatureResult `json:"author"`
	Committer *SignatureResult `json:"committer"`
	Message   string           `json:"message"`
}

A CommitResult represents a git commit.

func (*CommitResult) String

func (r *CommitResult) String() string

type ContextCallback

type ContextCallback func(ctx context.Context) context.Context

ContextCallback is invoked by GitServer at the beginning of each request. It allows for callers to create a context wrapper.

type GitCommand

type GitCommand struct {
	Old, New         *git.Oid
	OldTree, NewTree *git.Oid
	ReferenceName    string
	Reference        *git.Reference
	// contains filtered or unexported fields
}

A GitCommand describes one of the smart protocol's update commands.

func SpliceCommit

func SpliceCommit(
	repository *git.Repository,
	commit, parentCommit *git.Commit,
	overrides map[string]io.Reader,
	descriptions []SplitCommitDescription,
	author, committer *git.Signature,
	referenceName string,
	reference *git.Reference,
	commitMessageTag string,
	newPackPath string,
	log log15.Logger,
) ([]*GitCommand, error)

SpliceCommit creates a packfile at newPackPath from a commit in a repository that will contain split commits based on the provided array of SplitCommitDescriptions and will create a merge commit based of the split commits.

Note that a lockfile is not acquired in this method since it's assumed that the caller already has acquired one.

func (*GitCommand) IsCreate

func (c *GitCommand) IsCreate() bool

IsCreate returns whether the command creates a ref.

func (*GitCommand) IsDelete

func (c *GitCommand) IsDelete() bool

IsDelete returns whether the command deletes a ref.

func (*GitCommand) IsStaleRequest

func (c *GitCommand) IsStaleRequest() bool

IsStaleRequest returns whether the command is requesting a stale operation: if this is a create command but the reference does exist, or it's not replacing the current branch's HEAD.

func (*GitCommand) IsUpdate

func (c *GitCommand) IsUpdate() bool

IsUpdate returns whether the command updates a ref.

func (*GitCommand) String

func (c *GitCommand) String() string

type GitOperation

type GitOperation int

A GitOperation describes the current operation

const (
	// OperationPull denotes a pull operation.
	OperationPull GitOperation = iota

	// OperationPush denotes a push operation.
	OperationPush

	// OperationBrowse denotes a browse request.
	OperationBrowse
)

func (GitOperation) String

func (o GitOperation) String() string

type GitProtocol

type GitProtocol struct {
	AuthCallback               AuthorizationCallback
	ReferenceDiscoveryCallback ReferenceDiscoveryCallback
	UpdateCallback             UpdateCallback
	PreprocessCallback         PreprocessCallback
	AllowNonFastForward        bool
	// contains filtered or unexported fields
}

A GitProtocol contains the callbacks needed to customize the behavior of GitServer.

func NewGitProtocol

func NewGitProtocol(
	authCallback AuthorizationCallback,
	referenceDiscoveryCallback ReferenceDiscoveryCallback,
	updateCallback UpdateCallback,
	preprocessCallback PreprocessCallback,
	allowNonFastForward bool,
	log log15.Logger,
) *GitProtocol

NewGitProtocol returns a new instance of GitProtocol.

func (*GitProtocol) PushPackfile

func (p *GitProtocol) PushPackfile(
	ctx context.Context,
	repository *git.Repository,
	lockfile *Lockfile,
	level AuthorizationLevel,
	commands []*GitCommand,
	r io.Reader,
) (updatedRefs []UpdatedRef, err, unpackErr error)

PushPackfile unpacks the provided packfile (provided as an io.Reader), and updates the refs provided as commands into the repository.

type KeyedPool added in v1.5.0

type KeyedPool struct {
	// contains filtered or unexported fields
}

KeyedPool is an implementation of a size-bounded set of of objects associated with a key. If the objects in the pool exceed the maximum size (with a default of 256), the least-recently-used item in the pool will be evicted. Two callbacks can be provided and will be invoked when a new object should atomically be created when calling Get() and a suitable object is not available, and when an object is evicted due to lack of space.

func NewKeyedPool added in v1.5.0

func NewKeyedPool(options KeyedPoolOptions) *KeyedPool

NewKeyedPool creates a new object pool with the provided options.

func (*KeyedPool) Clear added in v1.5.0

func (c *KeyedPool) Clear()

Clear removes all stored items from the pool.

func (*KeyedPool) Get added in v1.5.0

func (c *KeyedPool) Get(key string) (interface{}, error)

Get obtains one element from the pool. If it was already present, the element is removed from the pool and returned. Otherwise, a new one will be created.

func (*KeyedPool) Len added in v1.5.0

func (c *KeyedPool) Len() int

Len returns the number of elements in the pool.

func (*KeyedPool) Put added in v1.5.0

func (c *KeyedPool) Put(key string, value interface{})

Put inserts an element into the pool. This operation could cause the least-recently-used element to be evicted.

func (*KeyedPool) Remove added in v1.5.0

func (c *KeyedPool) Remove(key string)

Remove removes the objects associated with the provided key from the pool.

type KeyedPoolOptions added in v1.5.0

type KeyedPoolOptions struct {
	// MaxEntries is the maximum number of items in the pool before an item is
	// evicted. The default is 256 if unset.
	MaxEntries int

	// Shards is the number of shards the pool will be split into to diminish
	// lock contention. The default is 16 if unset.
	Shards int

	// New is a callback that will be invoked if Get() does not find a
	// previously-created object in the pool.
	New func(key string) (interface{}, error)

	// OnEvicted is a callback that will be invoked when an object is evicted
	// from the pool.
	OnEvicted func(key string, value interface{})
}

KeyedPoolOptions are options that can be passed to NewKeyedPool to customize the pool limits and functionality.

type Lockfile

type Lockfile struct {
	// contains filtered or unexported fields
}

Lockfile represents a file-based lock that can be up/downgraded. Since this is using the flock(2) system call and the promotion/demotion is non-atomic, any attempt to change the lock type must verify any preconditions after calling Lock()/RLock().

func NewLockfile

func NewLockfile(repositoryPath string) *Lockfile

NewLockfile creates a new Lockfile that is initially unlocked.

func (*Lockfile) Lock

func (l *Lockfile) Lock() error

Lock acquires an exclusive lock for the Lockfile's path. Only one process / goroutine may hold an exclusive lock for this Lockfile's path at any given time.

func (*Lockfile) RLock

func (l *Lockfile) RLock() error

RLock acquires a shared lock for the Lockfile's path. More than one process / goroutine may hold a shared lock for this Lockfile's path at any given time.

func (*Lockfile) TryLock

func (l *Lockfile) TryLock() (bool, error)

TryLock attempts to acquire an exclusive lock for the Lockfile's path and returns whether it was able to do so. Only one process / goroutine may hold an exclusive lock for this Lockfile's path at any given time.

func (*Lockfile) TryRLock

func (l *Lockfile) TryRLock() (bool, error)

TryRLock attempts to acquires a shared lock for the Lockfile's path. More than one process / goroutine may hold a shared lock for this Lockfile's path at any given time.

func (*Lockfile) Unlock

func (l *Lockfile) Unlock() error

Unlock releases a lock for the Lockfile's path.

type LogResult

type LogResult struct {
	Log  []*CommitResult `json:"log,omitempty"`
	Next string          `json:"next,omitempty"`
}

A LogResult represents the result of a git log operation.

func (*LogResult) String

func (r *LogResult) String() string

type PackfileEntry

type PackfileEntry struct {
	Oid    git.Oid
	CRC    uint32
	Offset uint64
	Size   uint64
	Type   git.ObjectType
}

A PackfileEntry represents one of the entries in an .idx file.

type PackfileIndex

type PackfileIndex struct {
	Fanout  [256]uint32
	Entries []PackfileEntry
}

A PackfileIndex represents the contents of an .idx file.

func ParseIndex

func ParseIndex(filename string, odb *git.Odb) (*PackfileIndex, error)

ParseIndex parses the index located at the supplied filename and returns its contents as a PackfileIndex. The format for this file is documented in https://github.com/git/git/blob/master/Documentation/technical/pack-format.txt

func UnpackPackfile

func UnpackPackfile(
	odb *git.Odb,
	r io.Reader,
	dir string,
	progressCallback func(git.TransferProgress) error,
) (*PackfileIndex, string, error)

UnpackPackfile parses the packfile, ensures that the it is valid, creates an index file in the specified directory, and returns the path of the packfile.

type PktLineReader

type PktLineReader struct {
	// contains filtered or unexported fields
}

A PktLineReader implements git pkt-line protocol on top of an io.Reader. The documentation for the protocol can be found in https://github.com/git/git/blob/master/Documentation/technical/protocol-common.txt

func NewPktLineReader

func NewPktLineReader(r io.Reader) *PktLineReader

NewPktLineReader creates a new pkt-line based on the supplied Reader.

func (*PktLineReader) ReadPktLine

func (r *PktLineReader) ReadPktLine() ([]byte, error)

ReadPktLine returns the next pkt-line. The special value of pkt-flush is represented by ErrFlush, to distinguish it from the empty pkt-line.

type PktLineResponse

type PktLineResponse struct {
	Line string
	Err  error
}

PktLineResponse represents an expected entry from PktLineReader.

func ComparePktLineResponse

func ComparePktLineResponse(
	r io.Reader,
	expectedResponse []PktLineResponse,
) ([]PktLineResponse, bool)

ComparePktLineResponse compares what is being read from the supplied Reader when interpreted by a PktLineReader against an expected list of PktLineResponses.

type PktLineWriter

type PktLineWriter struct {
	// contains filtered or unexported fields
}

A PktLineWriter implements git pkt-line protocol on top of an io.Writer. The documentation for the protocol can be found in https://github.com/git/git/blob/master/Documentation/technical/protocol-common.txt

func NewPktLineWriter

func NewPktLineWriter(w io.Writer) *PktLineWriter

NewPktLineWriter creates a new pkt-line based on the supplied Writer.

func (*PktLineWriter) Close

func (w *PktLineWriter) Close() error

Close sends a flush-pkt.

func (*PktLineWriter) Flush

func (w *PktLineWriter) Flush() error

Flush sends a flush-pkt, which is a special value in the pkt-line protocol.

func (*PktLineWriter) WritePktLine

func (w *PktLineWriter) WritePktLine(data []byte) error

WritePktLine sends one pkt-line.

type PreprocessCallback

type PreprocessCallback func(
	ctx context.Context,
	repository *git.Repository,
	tmpDir string,
	packPath string,
	commands []*GitCommand,
) (string, []*GitCommand, error)

PreprocessCallback is invoked by GitServer when a user attempts to update a repository. It can perform an arbitrary transformation of the packfile and the update commands to be performed. A temporary directory is provided so that the new packfile can be stored there, if needed, and will be deleted afterwards. It returns the path of the new packfile, a new list of commands, and an error in case the operation failed.

type RefResult

type RefResult struct {
	Value  string `json:"value,omitempty"`
	Peeled string `json:"peeled,omitempty"`
	Target string `json:"target,omitempty"`
}

A RefResult represents a single reference in a git repository.

type ReferenceDiscovery

type ReferenceDiscovery struct {
	References   map[string]git.Oid
	Capabilities Capabilities
	HeadSymref   string
}

A ReferenceDiscovery represents the result of the reference discovery negotiation in git's pack protocol.

func DiscoverReferences

func DiscoverReferences(r io.Reader) (*ReferenceDiscovery, error)

DiscoverReferences returns the result of the reference discovery negotiation in git's pack protocol. This negotiation is documented in https://github.com/git/git/blob/master/Documentation/technical/pack-protocol.txt

type ReferenceDiscoveryCallback

type ReferenceDiscoveryCallback func(
	ctx context.Context,
	repository *git.Repository,
	referenceName string,
) bool

ReferenceDiscoveryCallback is invoked by GitServer when performing reference discovery or prior to updating a reference. It returhn whether the provided reference should be visible to the user.

type RefsResult

type RefsResult map[string]*RefResult

A RefsResult represents the mapping of ref names to RefResult.

func (*RefsResult) String

func (r *RefsResult) String() string

type SignatureResult

type SignatureResult struct {
	Name  string `json:"name"`
	Email string `json:"email"`
	Time  string `json:"time"`
}

A SignatureResult represents one of the signatures of the commit.

type SplitCommitDescription

type SplitCommitDescription struct {
	PathRegexps   []*regexp.Regexp
	ParentCommit  *git.Commit
	ReferenceName string
	Reference     *git.Reference
}

SplitCommitDescription describes the contents of a split commit.

func (*SplitCommitDescription) ContainsPath

func (s *SplitCommitDescription) ContainsPath(path string) bool

ContainsPath returns whether a SplitCommitDescription contains a regexp that matches a particular path.

type SplitCommitResult

type SplitCommitResult struct {
	CommitID *git.Oid
	TreeID   *git.Oid
}

SplitCommitResult contains the result of a split operation. It contains the git commit hash and its associated tree hash.

func SplitCommit

func SplitCommit(
	originalCommit *git.Commit,
	originalRepository *git.Repository,
	descriptions []SplitCommitDescription,
	repository *git.Repository,
	author, committer *git.Signature,
	commitMessageTag string,
	log log15.Logger,
) ([]SplitCommitResult, error)

SplitCommit splits a commit into several commits, based on the provided descriptions. The new commit will be added to a potentially different repository than the one it was originally created on.

type TreeEntryResult

type TreeEntryResult struct {
	Mode git.Filemode `json:"mode"`
	Type string       `json:"type"`
	ID   string       `json:"id"`
	Name string       `json:"name"`
	Size int64        `json:"size"`
}

A TreeEntryResult represents one entry in a git tree.

type TreeResult

type TreeResult struct {
	ID      string             `json:"id"`
	Entries []*TreeEntryResult `json:"entries"`
}

A TreeResult represents a git tree.

func (*TreeResult) String

func (r *TreeResult) String() string

type UpdateCallback

type UpdateCallback func(
	ctx context.Context,
	repository *git.Repository,
	level AuthorizationLevel,
	command *GitCommand,
	oldCommit, newCommit *git.Commit,
) error

UpdateCallback is invoked by GitServer when a user attempts to update a repository. It returns an error if the update request is invalid.

type UpdatedRef

type UpdatedRef struct {
	Name     string `json:"name"`
	From     string `json:"from"`
	To       string `json:"to"`
	FromTree string `json:"from_tree"`
	ToTree   string `json:"to_tree"`
}

An UpdatedRef describes a reference that was updated.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL