0
# FTP Server
1
2
Complete FTP server implementation with user management, permission system, connection limiting, and protocol command handlers. The server supports SSL/TLS encryption, custom path I/O backends, throttling, and extensive configuration options for production deployments.
3
4
## Capabilities
5
6
### Server Configuration and Lifecycle
7
8
Core server functionality including initialization, startup, and shutdown operations with comprehensive configuration options.
9
10
```python { .api }
11
class Server:
12
"""Main FTP server implementation with full protocol support."""
13
14
def __init__(self, users=None, *, block_size: int = 8192,
15
socket_timeout: float = None, idle_timeout: float = None,
16
wait_future_timeout: float = 1, path_timeout: float = None,
17
path_io_factory=PathIO, maximum_connections: int = None,
18
read_speed_limit: int = None, write_speed_limit: int = None,
19
read_speed_limit_per_connection: int = None,
20
write_speed_limit_per_connection: int = None,
21
ipv4_pasv_forced_response_address: str = None,
22
data_ports: Iterable[int] = None, encoding: str = "utf-8",
23
ssl: ssl.SSLContext = None):
24
"""
25
Initialize FTP server with configuration options.
26
27
Parameters:
28
- users: List of User objects or MemoryUserManager instance
29
- block_size: Default transfer block size in bytes
30
- socket_timeout: Socket operation timeout in seconds
31
- idle_timeout: Connection idle timeout in seconds
32
- wait_future_timeout: Future wait timeout in seconds
33
- path_timeout: Path operation timeout in seconds
34
- path_io_factory: PathIO factory class for filesystem operations
35
- maximum_connections: Maximum concurrent connections
36
- read_speed_limit: Global read speed limit in bytes/second
37
- write_speed_limit: Global write speed limit in bytes/second
38
- read_speed_limit_per_connection: Per-connection read limit
39
- write_speed_limit_per_connection: Per-connection write limit
40
- ipv4_pasv_forced_response_address: Fixed IP for PASV responses
41
- data_ports: Port range for data connections
42
- encoding: Text encoding for FTP commands
43
- ssl: SSL context for FTPS connections
44
"""
45
46
async def start(self, host: str = None, port: int = 0, **kwargs) -> None:
47
"""
48
Start the FTP server without blocking.
49
50
Parameters:
51
- host: Host interface to bind to (all interfaces if None)
52
- port: Port to bind to (0 for auto-assignment)
53
- **kwargs: Additional arguments passed to asyncio.start_server
54
"""
55
56
async def serve_forever(self) -> None:
57
"""Run server indefinitely, serving client connections."""
58
59
async def run(self, host: str = None, port: int = 0, **kwargs) -> None:
60
"""
61
Start server and run forever (combines start + serve_forever).
62
63
Parameters:
64
- host: Host interface to bind to
65
- port: Port to bind to
66
- **kwargs: Additional arguments passed to start_server
67
"""
68
69
async def close(self) -> None:
70
"""Close the server and all active connections."""
71
72
@property
73
def address(self) -> tuple[Union[str, None], int]:
74
"""
75
Get server address (host, port).
76
77
Returns:
78
Tuple of (host, port) where host may be None
79
"""
80
```
81
82
### User Management System
83
84
User account management with authentication, permissions, and connection limiting.
85
86
```python { .api }
87
class User:
88
"""User account with credentials, permissions, and limits."""
89
90
def __init__(self, login: str = None, password: str = None,
91
base_path: Path = Path("."), home_path: PurePosixPath = PurePosixPath("/"),
92
permissions: list[Permission] = None, maximum_connections: int = None,
93
read_speed_limit: int = None, write_speed_limit: int = None,
94
read_speed_limit_per_connection: int = None,
95
write_speed_limit_per_connection: int = None):
96
"""
97
Initialize user account.
98
99
Parameters:
100
- login: Username for authentication (None for anonymous)
101
- password: Password for authentication
102
- base_path: Local filesystem base path for user access
103
- home_path: Virtual home directory path for user
104
- permissions: List of Permission objects defining access rights
105
- maximum_connections: Maximum concurrent connections for this user
106
- read_speed_limit: Read speed limit for this user (bytes/second)
107
- write_speed_limit: Write speed limit for this user (bytes/second)
108
- read_speed_limit_per_connection: Per-connection read limit
109
- write_speed_limit_per_connection: Per-connection write limit
110
"""
111
112
async def get_permissions(self, path: PurePosixPath) -> Permission:
113
"""
114
Get effective permissions for a path.
115
116
Parameters:
117
- path: Path to check permissions for
118
119
Returns:
120
Permission object with read/write access flags
121
"""
122
123
class Permission:
124
"""Path permission specification defining access rights."""
125
126
def __init__(self, path: str = "/", readable: bool = True, writable: bool = True):
127
"""
128
Initialize path permission.
129
130
Parameters:
131
- path: Path pattern this permission applies to
132
- readable: Allow read operations on this path
133
- writable: Allow write operations on this path
134
"""
135
136
def is_parent(self, other: PurePosixPath) -> bool:
137
"""
138
Check if this permission is a parent of another path.
139
140
Parameters:
141
- other: Path to check against
142
143
Returns:
144
True if this permission covers the given path
145
"""
146
```
147
148
### User Manager Interface
149
150
Abstract interface for custom user authentication systems.
151
152
```python { .api }
153
class AbstractUserManager:
154
"""Abstract base class for user management systems."""
155
156
class GetUserResponse(Enum):
157
"""Response codes for user lookup operations."""
158
OK = "ok"
159
PASSWORD_REQUIRED = "password_required"
160
ERROR = "error"
161
162
async def get_user(self, login: str) -> tuple[GetUserResponse, Union[User, None], str]:
163
"""
164
Retrieve user by login name.
165
166
Parameters:
167
- login: Username to look up
168
169
Returns:
170
Tuple of (response_code, user_object, message)
171
"""
172
173
async def authenticate(self, user: User, password: str) -> bool:
174
"""
175
Authenticate user with provided password.
176
177
Parameters:
178
- user: User object to authenticate
179
- password: Password to verify
180
181
Returns:
182
True if authentication successful, False otherwise
183
"""
184
185
async def notify_logout(self, user: User) -> None:
186
"""
187
Notify manager that user has logged out.
188
189
Parameters:
190
- user: User object that logged out
191
"""
192
193
class MemoryUserManager(AbstractUserManager):
194
"""Built-in user manager for predefined users."""
195
196
def __init__(self, users: list[User], timeout: float = None):
197
"""
198
Initialize with list of users.
199
200
Parameters:
201
- users: List of User objects to manage
202
- timeout: Authentication timeout in seconds
203
"""
204
205
async def get_user(self, login: str) -> tuple[GetUserResponse, Union[User, None], str]:
206
"""Look up user in memory store."""
207
208
async def authenticate(self, user: User, password: str) -> bool:
209
"""Authenticate against stored password."""
210
211
async def notify_logout(self, user: User) -> None:
212
"""Handle user logout notification."""
213
```
214
215
### Connection Management
216
217
Connection limiting and state management utilities.
218
219
```python { .api }
220
class AvailableConnections:
221
"""Semaphore-like object for managing connection limits."""
222
223
def __init__(value: int = None):
224
"""
225
Initialize connection limiter.
226
227
Parameters:
228
- value: Maximum number of connections (None for unlimited)
229
"""
230
231
def locked(self) -> bool:
232
"""
233
Check if connection limit is reached.
234
235
Returns:
236
True if no more connections allowed
237
"""
238
239
def acquire(self) -> None:
240
"""Acquire a connection slot (may block)."""
241
242
def release(self) -> None:
243
"""Release a connection slot."""
244
```
245
246
### Command Decorators
247
248
Decorator classes for FTP command validation and processing.
249
250
```python { .api }
251
class ConnectionConditions:
252
"""Decorator for validating connection state before command execution."""
253
254
class PathConditions:
255
"""Decorator for validating path existence and type before operations."""
256
257
class PathPermissions:
258
"""Decorator for checking user permissions before path operations."""
259
260
def worker(func):
261
"""
262
Decorator making FTP command handlers abortable.
263
264
Parameters:
265
- func: FTP command handler function
266
267
Returns:
268
Wrapped function with abort support
269
"""
270
```
271
272
## Usage Examples
273
274
### Basic FTP Server
275
276
```python
277
import aioftp
278
import asyncio
279
from pathlib import Path
280
281
async def basic_server():
282
# Create users
283
users = [
284
aioftp.User(
285
login="admin",
286
password="secret",
287
base_path=Path("/srv/ftp"),
288
permissions=[
289
aioftp.Permission("/", readable=True, writable=True),
290
]
291
),
292
aioftp.User( # Anonymous user
293
base_path=Path("/srv/ftp/public"),
294
permissions=[
295
aioftp.Permission("/", readable=True, writable=False),
296
]
297
)
298
]
299
300
# Create and run server
301
server = aioftp.Server(users=users)
302
await server.run(host="localhost", port=2121)
303
304
asyncio.run(basic_server())
305
```
306
307
### Server with SSL/TLS
308
309
```python
310
import aioftp
311
import asyncio
312
import ssl
313
from pathlib import Path
314
315
async def secure_server():
316
# Create SSL context
317
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
318
ssl_context.load_cert_chain("server.crt", "server.key")
319
320
# Create user
321
user = aioftp.User(
322
login="secure_user",
323
password="secure_pass",
324
base_path=Path("/secure/ftp"),
325
permissions=[aioftp.Permission("/", readable=True, writable=True)]
326
)
327
328
# Create FTPS server
329
server = aioftp.Server(
330
users=[user],
331
ssl=ssl_context,
332
maximum_connections=10,
333
read_speed_limit=1024*1024, # 1MB/s
334
write_speed_limit=1024*1024
335
)
336
337
await server.run(host="0.0.0.0", port=990) # FTPS implicit port
338
339
asyncio.run(secure_server())
340
```
341
342
### Custom User Manager
343
344
```python
345
import aioftp
346
import asyncio
347
from pathlib import Path
348
349
class DatabaseUserManager(aioftp.AbstractUserManager):
350
"""Example custom user manager using database."""
351
352
async def get_user(self, login: str):
353
# Query database for user
354
if login in self.valid_users:
355
user = aioftp.User(
356
login=login,
357
base_path=Path(f"/users/{login}"),
358
permissions=[aioftp.Permission("/", readable=True, writable=True)]
359
)
360
return (self.GetUserResponse.PASSWORD_REQUIRED, user, "")
361
return (self.GetUserResponse.ERROR, None, "User not found")
362
363
async def authenticate(self, user: aioftp.User, password: str) -> bool:
364
# Verify password against database
365
return self.verify_password_hash(user.login, password)
366
367
async def notify_logout(self, user: aioftp.User) -> None:
368
# Log user logout
369
print(f"User {user.login} logged out")
370
371
async def custom_server():
372
user_manager = DatabaseUserManager()
373
server = aioftp.Server(users=user_manager)
374
await server.run(host="localhost", port=21)
375
376
asyncio.run(custom_server())
377
```
378
379
### Advanced Server Configuration
380
381
```python
382
import aioftp
383
import asyncio
384
from pathlib import Path
385
386
async def advanced_server():
387
# Multiple users with different permissions
388
users = [
389
aioftp.User(
390
login="admin",
391
password="admin_pass",
392
base_path=Path("/srv/ftp"),
393
permissions=[
394
aioftp.Permission("/", readable=True, writable=True),
395
aioftp.Permission("/logs", readable=True, writable=False),
396
],
397
maximum_connections=5,
398
read_speed_limit=2*1024*1024, # 2MB/s
399
write_speed_limit=1*1024*1024 # 1MB/s
400
),
401
aioftp.User(
402
login="upload_only",
403
password="upload_pass",
404
base_path=Path("/srv/ftp/uploads"),
405
permissions=[
406
aioftp.Permission("/", readable=False, writable=True),
407
],
408
maximum_connections=2
409
)
410
]
411
412
# Advanced server configuration
413
server = aioftp.Server(
414
users=users,
415
maximum_connections=20,
416
socket_timeout=30.0,
417
idle_timeout=300.0,
418
data_ports=range(20000, 20100), # Custom data port range
419
encoding="utf-8",
420
ipv4_pasv_forced_response_address="192.168.1.100" # NAT support
421
)
422
423
await server.run(host="0.0.0.0", port=21)
424
425
asyncio.run(advanced_server())
426
```
427
428
## Types
429
430
```python { .api }
431
# Server-related type aliases
432
UserManagerType = Union[list[User], AbstractUserManager]
433
434
# Connection state container
435
class Connection(defaultdict[str, asyncio.Future]):
436
"""Connection state with futures for async coordination."""
437
```