gftp/actor
OTP actor wrapper for gftp that serializes all FTP operations.
Wrapping an FtpClient in an actor prevents protocol state corruption
by ensuring that control commands are rejected while a data channel is open.
Usage
import gftp
import gftp/actor as ftp_actor
let assert Ok(client) = gftp.connect("ftp.example.com", 21)
let assert Ok(started) = ftp_actor.start(client)
let handle = started.data
let assert Ok(_) = ftp_actor.login(handle, "user", "password")
let assert Ok(cwd) = ftp_actor.pwd(handle)
let assert Ok(_) = ftp_actor.quit(handle)
Message-based streaming
The actor enforces chunk protection: once a data channel is opened via
open_retr, open_stor, etc., all control commands return
Error(DataTransferInProgress) until close_data_channel is called.
let assert Ok(data_stream) = ftp_actor.open_retr(handle, "file.txt")
// ... receive data via stream.receive_next_packet_as_message ...
let assert Ok(_) = ftp_actor.close_data_channel(handle, data_stream)
Types
Handle to an FTP actor. Use the public functions in this module to interact with it.
The handle wraps the actor subject and a configurable call timeout (default 30 seconds).
Use with_call_timeout to adjust the timeout for all subsequent calls.
pub opaque type Handle
Values
pub fn appe(
handle: Handle,
path: String,
writer: fn(stream.DataStream) -> Result(Nil, result.FtpError),
) -> Result(Nil, result.FtpError)
Append to a file using a callback.
pub fn clear_command_channel(
handle: Handle,
) -> Result(Nil, result.FtpError)
Clear the command channel encryption.
pub fn close_data_channel(
handle: Handle,
data_stream: stream.DataStream,
) -> Result(Nil, result.FtpError)
Close a data channel and finalize the transfer.
pub fn custom_command(
handle: Handle,
command_str: String,
expected_statuses: List(status.Status),
) -> Result(response.Response, result.FtpError)
Execute a custom FTP command.
pub fn custom_data_command(
handle: Handle,
command_str: String,
expected_statuses: List(status.Status),
on_data_stream: fn(stream.DataStream, response.Response) -> Result(
Nil,
result.FtpError,
),
) -> Result(Nil, result.FtpError)
Execute a custom data command with a callback.
pub fn cwd(
handle: Handle,
path: String,
) -> Result(Nil, result.FtpError)
Change working directory.
pub fn dele(
handle: Handle,
path: String,
) -> Result(Nil, result.FtpError)
Delete a file.
pub fn eprt(
handle: Handle,
address: String,
port: Int,
ip_version: mode.IpVersion,
) -> Result(Nil, result.FtpError)
Execute an EPRT command.
pub fn feat(
handle: Handle,
) -> Result(
dict.Dict(String, option.Option(String)),
result.FtpError,
)
Retrieve server features (FEAT command).
pub fn into_secure(
handle: Handle,
ssl_options: kafein.WrapOptions,
) -> Result(Nil, result.FtpError)
Switch to explicit secure mode (FTPS).
pub fn list(
handle: Handle,
pathname: option.Option(String),
) -> Result(List(String), result.FtpError)
List directory contents.
pub fn login(
handle: Handle,
username: String,
password: String,
) -> Result(Nil, result.FtpError)
Log in to the FTP server.
pub fn mdtm(
handle: Handle,
pathname: String,
) -> Result(timestamp.Timestamp, result.FtpError)
Get the modification time of a file.
pub fn mkd(
handle: Handle,
path: String,
) -> Result(Nil, result.FtpError)
Create a directory.
pub fn mlsd(
handle: Handle,
pathname: option.Option(String),
) -> Result(List(String), result.FtpError)
Machine-readable directory listing.
pub fn mlst(
handle: Handle,
pathname: option.Option(String),
) -> Result(String, result.FtpError)
Execute an MLST command.
pub fn nlst(
handle: Handle,
pathname: option.Option(String),
) -> Result(List(String), result.FtpError)
List file names only.
pub fn open_appe(
handle: Handle,
path: String,
) -> Result(stream.DataStream, result.FtpError)
Open a data channel for appending to a file.
The returned DataStream has its controlling process set to the caller,
so message-based I/O (receive_next_packet_as_message) works correctly.
pub fn open_data_command(
handle: Handle,
command_str: String,
expected_statuses: List(status.Status),
) -> Result(
#(stream.DataStream, response.Response),
result.FtpError,
)
Open a data channel for a custom command.
The returned DataStream has its controlling process set to the caller,
so message-based I/O (receive_next_packet_as_message) works correctly.
pub fn open_list(
handle: Handle,
pathname: option.Option(String),
) -> Result(stream.DataStream, result.FtpError)
Open a data channel for a LIST directory listing.
The returned DataStream has its controlling process set to the caller,
so message-based I/O (receive_next_packet_as_message) works correctly.
pub fn open_mlsd(
handle: Handle,
pathname: option.Option(String),
) -> Result(stream.DataStream, result.FtpError)
Open a data channel for an MLSD machine-readable listing.
The returned DataStream has its controlling process set to the caller,
so message-based I/O (receive_next_packet_as_message) works correctly.
pub fn open_nlst(
handle: Handle,
pathname: option.Option(String),
) -> Result(stream.DataStream, result.FtpError)
Open a data channel for an NLST file name listing.
The returned DataStream has its controlling process set to the caller,
so message-based I/O (receive_next_packet_as_message) works correctly.
pub fn open_retr(
handle: Handle,
path: String,
) -> Result(stream.DataStream, result.FtpError)
Open a data channel for downloading a file.
The returned DataStream has its controlling process set to the caller,
so message-based I/O (receive_next_packet_as_message) works correctly.
pub fn open_stor(
handle: Handle,
path: String,
) -> Result(stream.DataStream, result.FtpError)
Open a data channel for uploading a file.
The returned DataStream has its controlling process set to the caller,
so message-based I/O (receive_next_packet_as_message) works correctly.
pub fn opts(
handle: Handle,
option: String,
value: option.Option(String),
) -> Result(Nil, result.FtpError)
Set a server option (OPTS command).
pub fn pwd(handle: Handle) -> Result(String, result.FtpError)
Get the current working directory.
pub fn quit(handle: Handle) -> Result(Nil, result.FtpError)
Quit the FTP session and stop the actor.
pub fn rename(
handle: Handle,
from: String,
to: String,
) -> Result(Nil, result.FtpError)
Rename a file.
pub fn rest(
handle: Handle,
offset: Int,
) -> Result(Nil, result.FtpError)
Set the restart offset for the next transfer.
pub fn retr(
handle: Handle,
path: String,
reader: fn(stream.DataStream) -> Result(Nil, result.FtpError),
) -> Result(Nil, result.FtpError)
Download a file using a callback.
pub fn rmd(
handle: Handle,
path: String,
) -> Result(Nil, result.FtpError)
Remove a directory.
pub fn site(
handle: Handle,
sub_command: String,
) -> Result(response.Response, result.FtpError)
Execute a SITE command.
pub fn size(
handle: Handle,
pathname: String,
) -> Result(Int, result.FtpError)
Get the size of a file in bytes.
pub fn start(
client: gftp.FtpClient,
) -> Result(actor.Started(Handle), actor.StartError)
Start an FTP actor wrapping the given client.
The client should already be connected (via gftp.connect).
All subsequent operations should go through the returned Handle.
You should never keep an active FtpClient around after starting the actor,
as it would allow bypassing the chunk protection and corrupting the protocol state.
Only interact with the FTP session via the returned Handle and the functions in this module.
pub fn stor(
handle: Handle,
path: String,
writer: fn(stream.DataStream) -> Result(Nil, result.FtpError),
) -> Result(Nil, result.FtpError)
Upload a file using a callback.
pub fn transfer_type(
handle: Handle,
file_type: file_type.FileType,
) -> Result(Nil, result.FtpError)
Set the file transfer type.
pub fn welcome_message(handle: Handle) -> option.Option(String)
Get the welcome message from the FTP server.
pub fn with_active_mode(handle: Handle, timeout: Int) -> Nil
Enable active mode with the specified data connection timeout.
pub fn with_call_timeout(handle: Handle, timeout: Int) -> Handle
Set the timeout in milliseconds for actor calls. Defaults to 30000 (30 seconds).
This timeout applies to all subsequent calls made through this handle.
For large file transfers via callback-based methods (retr, stor, appe),
consider increasing this value since the actor call blocks until the callback completes.
let handle = ftp_actor.with_call_timeout(handle, 120_000)
pub fn with_nat_workaround(handle: Handle, enabled: Bool) -> Nil
Enable or disable the NAT workaround for passive mode.
pub fn with_passive_stream_builder(
handle: Handle,
builder: fn(String, Int) -> Result(
stream.DataStream,
result.FtpError,
),
) -> Nil
Set a custom passive stream builder.