or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client.mderrors.mdindex.mdpathio.mdserver.mdstreaming.md

server.mddocs/

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

```