graval

package module
v1.0.9 Latest Latest
Warning

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

Go to latest
Published: Jun 17, 2021 License: MIT Imports: 18 Imported by: 0

README

graval Test

An experimental FTP server framework. By providing a simple driver class that responds to a handful of methods you can have a complete FTP server.

Some sample use cases include persisting data to:

  • an Amazon S3 bucket
  • a Google cloud storage bucket
  • a relational database
  • redis
  • memory

There is a sample in-memory driver available - see the usage instructions below for the steps to use it.

Full documentation for the package is available on godoc

Installation

go get github.com/UnAfraid/graval

Usage

To boot an FTP server you will need to provide a driver that speaks to your persistence layer - the required driver contract is listed below.

There is a sample in-memory driver available as a demo. You can build it with this command:

go install github.com/UnAfraid/graval/graval-mem

Then run it:

./bin/graval-mem

And finally, connect to the server with any FTP client and the following details:

host: 127.0.0.1
username: test
password: 1234
The Driver Contract

Your driver MUST implement a number of simple methods. You can view the required contract in the package docs on godoc

Contributors

Warning

FTP is an incredibly insecure protocol. Be careful about forcing users to authenticate with a username or password that are important.

License

This library is distributed under the terms of the MIT License. See the included file for more detail.

Contributing

All suggestions and patches welcome, preferably via a git repository I can pull from. If this library proves useful to you, please let me know.

Further Reading

There are a range of RFCs that together specify the FTP protocol. In chronological order, the more useful ones are:

For an english summary that's somewhat more legible than the RFCs, and provides some commentary on what features are actually useful or relevant 24 years after RFC959 was published:

For a history lesson, check out Appendix III of RCF959. It lists the preceding (obsolete) RFC documents that relate to file transfers, including the ye old RFC114 from 1971, "A File Transfer Protocol"

This library is heavily based on em-ftpd, an FTPd framework with similar design goals within the ruby and EventMachine ecosystems. It worked well enough, but you know, callbacks and event loops make me something.

Documentation

Overview

An experimental FTP server framework. By providing a simple driver class that responds to a handful of methods you can have a complete FTP server.

Some sample use cases include persisting data to an Amazon S3 bucket, a relational database, redis or memory.

There is a sample in-memory driver available - see the documentation for the graval-mem package or the graval READEME for more details.

Index

Constants

This section is empty.

Variables

View Source
var FtpLogTimeFormat = "2006-01-02 15:04:05"

Functions

func NewDirItem

func NewDirItem(name string, modtime time.Time) os.FileInfo

NewDirItem creates a new os.FileInfo that represents a single diretory. Use this function to build the response to DirContents() in your FTPDriver implementation.

func NewFileItem

func NewFileItem(name string, bytes int64, modtime time.Time) os.FileInfo

NewFileItem creates a new os.FileInfo that represents a single file. Use this function to build the response to DirContents() in your FTPDriver implementation.

Types

type FTPDriver

type FTPDriver interface {
	// params  - username, password
	// returns - true if the provided details are valid
	Authenticate(string, string, string) (bool, error)

	// params  - a file path
	// returns - an int with the number of bytes in the file or -1 if the file
	//           doesn't exist
	Bytes(string) (int64, error)

	// params  - a file path
	// returns - a time indicating when the requested path was last modified
	//         - an error if the file doesn't exist or the user lacks
	//           permissions
	ModifiedTime(string) (time.Time, error)

	// params  - path
	// returns - true if the current user is permitted to change to the
	//           requested path
	ChangeDir(string) (bool, error)

	// params  - path
	// returns - a collection of items describing the contents of the requested
	//           path
	DirContents(string) ([]os.FileInfo, error)

	// params  - path
	// returns - true if the directory was deleted
	DeleteDir(string) (bool, error)

	// params  - path
	// returns - true if the file was deleted
	DeleteFile(string) (bool, error)

	// params  - from_path, to_path
	// returns - true if the file was renamed
	Rename(string, string) (bool, error)

	// params  - path
	// returns - true if the new directory was created
	MakeDir(string) (bool, error)

	// params  - path
	// returns - a Reader that will return file data to send to the client
	GetFile(string) (io.ReadCloser, error)

	// params  - desination path, an io.Reader containing the file data
	// returns - true if the data was successfully persisted
	PutFile(string, io.Reader) (bool, error)
}

You will create an implementation of this interface that speaks to your chosen persistence layer. graval will create a new instance of your driver for each client that connects and delegate to it as required.

type FTPDriverFactory

type FTPDriverFactory interface {
	NewDriver() (FTPDriver, error)
}

For each client that connects to the server, a new FTPDriver is required. Create an implementation if this interface and provide it to FTPServer.

type FTPLogger

type FTPLogger interface {
	Info(args ...interface{})
	Infof(format string, args ...interface{})
	Warn(args ...interface{})
	Warnf(format string, args ...interface{})
	Error(args ...interface{})
	Errorf(format string, args ...interface{})
	Debug(args ...interface{})
	Debugf(format string, args ...interface{})
}

func NewDefaultFtpLogger

func NewDefaultFtpLogger() FTPLogger

func NewDefaultFtpLoggerWithLevel

func NewDefaultFtpLoggerWithLevel(ftpLogLevel FtpLogLevel) FTPLogger

type FTPServer

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

FTPServer is the root of your FTP application. You should instantiate one of these and call ListenAndServe() to start accepting client connections.

Always use the NewFTPServer() method to create a new FTPServer.

func NewFTPServer

func NewFTPServer(opts *FTPServerOpts) *FTPServer

NewFTPServer initialises a new FTP server. Configuration options are provided via an instance of FTPServerOpts. Calling this function in your code will probably look something like this:

factory := &MyDriverFactory{}
server  := graval.NewFTPServer(&graval.FTPServerOpts{ Factory: factory })

or:

factory := &MyDriverFactory{}
opts    := &graval.FTPServerOpts{
  Factory: factory,
  Port: 2000,
  Hostname: "127.0.0.1",
}
server  := graval.NewFTPServer(opts)

func (*FTPServer) Close

func (ftpServer *FTPServer) Close()

Close signals the server to stop. It may take a couple of seconds. Do not call ListenAndServe again after this, build a new FTPServer.

func (*FTPServer) ListenAndServe

func (ftpServer *FTPServer) ListenAndServe() error

ListenAndServe asks a new FTPServer to begin accepting client connections. It accepts no arguments - all configuration is provided via the NewFTPServer function.

If the server fails to start for any reason, an error will be returned. Common errors are trying to bind to a privileged port or something else is already listening on the same port.

type FTPServerOpts

type FTPServerOpts struct {
	// Server name will be used for welcome message
	ServerName string

	// The factory that will be used to create a new FTPDriver instance for
	// each client connection. This is a mandatory option.
	Factory FTPDriverFactory

	// The hostname that the FTP server should listen on. Optional, defaults to
	// "::", which means all hostnames on ipv4 and ipv6.
	Hostname string

	// The port that the FTP should listen on. Optional, defaults to 3000. In
	// a production environment you will probably want to change this to 21.
	Port uint16

	// The lower bound of port numbers that can be used for passive-mode data sockets
	// Defaults to 0, which allows the server to pick any free port
	PasvMinPort uint16

	// The upper bound of port numbers that can be used for passive-mode data sockets
	// Defaults to 0, which allows the server to pick any free port
	PasvMaxPort uint16

	// Use this option to override the IP address that will be advertised in response to the
	// PASV command. Most setups can ignore this, but it can be helpful in situations where
	// the FTP server is behind a NAT gateway or load balancer and the public IP used by
	// clients is different to the IP the server is directly listening on
	PasvAdvertisedIp string

	// The logger implementation
	Logger FTPLogger
}

serverOpts contains parameters for graval.NewFTPServer()

type FtpLogLevel

type FtpLogLevel int
const (
	ErrorLevel FtpLogLevel = iota
	WarnLevel
	InfoLevel
	DebugLevel
)

Directories

Path Synopsis
An example FTP server build on top of go-raval.
An example FTP server build on top of go-raval.

Jump to

Keyboard shortcuts

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