.. server_tutorial:

Server tutorial
===============

aioftp server is much more like a tool. You configure it, run and forget about
it.

Configuring server
------------------

At first you should create :class:`aioftp.Server` instance and start it

:py:meth:`aioftp.Server.start`

::

    >>> server = aioftp.Server()
    >>> await server.start()

Default arguments allow anonymous login and read/write current directory. So,
there is one user with anonymous login and read/write permissions on "/"
virtual path. Real path is current working directory.

Dealing with users and permissions
----------------------------------

You can specify as much users as you want, just pass list of them when creating
:class:`aioftp.Server` instance

:class:`aioftp.User`

:class:`aioftp.Permission`

::

    >>> users = (
    ...     aioftp.User(
    ...         "Guido",
    ...         "secret_password",
    ...         home_path="/Guido",
    ...         permissions=(
    ...             aioftp.Permission("/", readable=False, writable=False),
    ...             aioftp.Permission("/Guido", readable=True, writable=True),
    ...         )
    ...     ),
    ...     aioftp.User(
    ...         home_path="/anon",
    ...         permissions=(
    ...             aioftp.Permission("/", readable=False, writable=False),
    ...             aioftp.Permission("/anon", readable=True),
    ...         )
    ...     ),
    ... )
    >>> server = aioftp.Server(users)
    >>> await server.start()

This will create two users: "Guido", who can read and write to "/Guido" folder,
which is home folder, but can't read/write the root and other directories and
anonymous user, who home directory is "/anon" and there is only read
permission.

Path abstraction layer
----------------------

aioftp provides abstraction of file system operations. You can use exist ones:

* :py:class:`aioftp.PathIO` — blocking path operations
* :py:class:`aioftp.AsyncPathIO` — non-blocking path operations, this one is
  blocking ones just wrapped with
  :py:meth:`asyncio.BaseEventLoop.run_in_executor`. It's really slow, so it's
  better to avoid usage of this path io layer.
* :py:class:`aioftp.MemoryPathIO` — in-memory realization of file system, this
  one is just proof of concept and probably not too fast (as it can be).

You can specify `path_io_factory` when creating :py:class:`aioftp.Server`
instance. Default factory is :py:class:`aioftp.PathIO`.

::

    >>> server = aioftp.Server(path_io_factory=aioftp.MemoryPathIO)
    >>> await server.start()

Dealing with timeouts
---------------------

There is three different timeouts you can specify:

* `socket_timeout` — timeout for low-level socket operations
  :py:meth:`asyncio.StreamReader.read`,
  :py:meth:`asyncio.StreamReader.readline` and
  :py:meth:`asyncio.StreamWriter.drain`. This one does not affects awaiting
  command read operation.
* `path_timeout` — timeout for file system operations
* `idle_timeout` — timeout for socket read operation when awaiting command,
  another words: how long user can keep silence without sending commands
* `wait_future_timeout` — timeout for waiting connection states (the main
  purpose is wait for passive connection)

Maximum connections
-------------------

Connections count can be specified:

* per server
* per user

First one via server constructor

::

    >>> server = aioftp.Server(maximum_connections=3)

Second one via user class

::

    >>> users = (aioftp.User(maximum_connections=3),)
    >>> server = aioftp.Server(users)

Throttle
--------

Server have many options for read/write speed throttle:

* global per server
* per connection
* global per user
* per user connection

"Global per server" and "per connection" can be provided by constructor

::

    >>> server = aioftp.Server(
    ...     read_speed_limit=1024 * 1024,
    ...     write_speed_limit=1024 * 1024,
    ...     read_speed_limit_per_connection=100 * 1024,
    ...     write_speed_limit_per_connection=100 * 1024
    ... )

User throttles can be provided by user constructor

::

    >>> users = (
    ...     aioftp.User(
    ...         read_speed_limit=1024 * 1024,
    ...         write_speed_limit=1024 * 1024,
    ...         read_speed_limit_per_connection=100 * 1024,
    ...         write_speed_limit_per_connection=100 * 1024
    ...     ),
    ... )
    >>> server = aioftp.Server(users)

Stopping the server
-------------------

::

    >>> await server.close()

WARNING
-------

:py:meth:`aioftp.Server.list` use :py:meth:`aioftp.Server.build_list_string`,
which should produce `LIST` strings with :py:meth:`datetime.datetime.strftime`.
For proper work (in part of formatting month abbreviation) locale should be
setted to "C". For this reason if you use multithreaded app, and use some
locale-dependent stuff, you should use :py:meth:`aioftp.setlocale` context
manager when you dealing with locale in another thread.

Futher reading
--------------
:doc:`server_api`
