pub struct StatusCode(/* private fields */);Expand description
HTTP status code.
Implementations§
Source§impl StatusCode
impl StatusCode
Sourcepub const CONTINUE: StatusCode
pub const CONTINUE: StatusCode
100 Continue
Sourcepub const SWITCHING_PROTOCOLS: StatusCode
pub const SWITCHING_PROTOCOLS: StatusCode
101 Switching Protocols
Sourcepub const OK: StatusCode
pub const OK: StatusCode
200 OK
Sourcepub const CREATED: StatusCode
pub const CREATED: StatusCode
201 Created
Sourcepub const ACCEPTED: StatusCode
pub const ACCEPTED: StatusCode
202 Accepted
Sourcepub const NO_CONTENT: StatusCode
pub const NO_CONTENT: StatusCode
204 No Content
Sourcepub const PARTIAL_CONTENT: StatusCode
pub const PARTIAL_CONTENT: StatusCode
206 Partial Content
Sourcepub const MOVED_PERMANENTLY: StatusCode
pub const MOVED_PERMANENTLY: StatusCode
301 Moved Permanently
Sourcepub const FOUND: StatusCode
pub const FOUND: StatusCode
302 Found
Sourcepub const SEE_OTHER: StatusCode
pub const SEE_OTHER: StatusCode
303 See Other
Sourcepub const NOT_MODIFIED: StatusCode
pub const NOT_MODIFIED: StatusCode
304 Not Modified
Sourcepub const TEMPORARY_REDIRECT: StatusCode
pub const TEMPORARY_REDIRECT: StatusCode
307 Temporary Redirect
Sourcepub const PERMANENT_REDIRECT: StatusCode
pub const PERMANENT_REDIRECT: StatusCode
308 Permanent Redirect
Sourcepub const BAD_REQUEST: StatusCode
pub const BAD_REQUEST: StatusCode
400 Bad Request
Sourcepub const UNAUTHORIZED: StatusCode
pub const UNAUTHORIZED: StatusCode
401 Unauthorized
Sourcepub const FORBIDDEN: StatusCode
pub const FORBIDDEN: StatusCode
403 Forbidden
Sourcepub const NOT_FOUND: StatusCode
pub const NOT_FOUND: StatusCode
404 Not Found
Sourcepub const METHOD_NOT_ALLOWED: StatusCode
pub const METHOD_NOT_ALLOWED: StatusCode
405 Method Not Allowed
Sourcepub const NOT_ACCEPTABLE: StatusCode
pub const NOT_ACCEPTABLE: StatusCode
406 Not Acceptable
Sourcepub const PRECONDITION_FAILED: StatusCode
pub const PRECONDITION_FAILED: StatusCode
412 Precondition Failed
Sourcepub const PAYLOAD_TOO_LARGE: StatusCode
pub const PAYLOAD_TOO_LARGE: StatusCode
413 Payload Too Large
Sourcepub const UNSUPPORTED_MEDIA_TYPE: StatusCode
pub const UNSUPPORTED_MEDIA_TYPE: StatusCode
415 Unsupported Media Type
Sourcepub const RANGE_NOT_SATISFIABLE: StatusCode
pub const RANGE_NOT_SATISFIABLE: StatusCode
416 Range Not Satisfiable
Sourcepub const UNPROCESSABLE_ENTITY: StatusCode
pub const UNPROCESSABLE_ENTITY: StatusCode
422 Unprocessable Entity
Sourcepub const TOO_MANY_REQUESTS: StatusCode
pub const TOO_MANY_REQUESTS: StatusCode
429 Too Many Requests
Sourcepub const CLIENT_CLOSED_REQUEST: StatusCode
pub const CLIENT_CLOSED_REQUEST: StatusCode
499 Client Closed Request
Sourcepub const INTERNAL_SERVER_ERROR: StatusCode
pub const INTERNAL_SERVER_ERROR: StatusCode
500 Internal Server Error
Sourcepub const SERVICE_UNAVAILABLE: StatusCode
pub const SERVICE_UNAVAILABLE: StatusCode
503 Service Unavailable
Sourcepub const GATEWAY_TIMEOUT: StatusCode
pub const GATEWAY_TIMEOUT: StatusCode
504 Gateway Timeout
Sourcepub const fn from_u16(code: u16) -> StatusCode
pub const fn from_u16(code: u16) -> StatusCode
Create a status code from a u16.
Sourcepub const fn as_u16(self) -> u16
pub const fn as_u16(self) -> u16
Get the numeric value.
Examples found in repository?
examples/hello_world.rs (line 99)
61fn main() {
62 println!("fastapi_rust Hello World Example");
63 println!("================================\n");
64
65 // Build the application
66 //
67 // App::builder() creates a new application builder that lets you:
68 // - Add routes for different HTTP methods
69 // - Configure middleware
70 // - Set application state
71 // - Define exception handlers
72 let app = App::builder()
73 // Register a GET handler for the root path "/"
74 //
75 // This is equivalent to:
76 // @app.get("/")
77 // def hello():
78 // return "Hello, World!"
79 // in Python FastAPI
80 .get("/", hello_handler)
81 // Build the final immutable App
82 .build();
83
84 println!("App created with {} route(s)\n", app.route_count());
85
86 // Create a test client to make requests to our app
87 //
88 // TestClient wraps any Handler (including App) and provides
89 // a convenient API for making HTTP requests in tests.
90 let client = TestClient::new(app);
91
92 // Make a GET request to "/"
93 println!("Making request: GET /");
94 let response = client.get("/").send();
95
96 // Check the response
97 println!(
98 "GET / -> {} {}",
99 response.status().as_u16(),
100 response.status().canonical_reason()
101 );
102 println!("Response: {}\n", response.text());
103
104 // Verify success
105 if !check_eq(response.status().as_u16(), 200, "GET / should return 200") {
106 return;
107 }
108 if !check_eq(response.text(), "Hello, World!", "GET / should return body") {
109 return;
110 }
111
112 // Try a path that doesn't exist - should get 404
113 println!("Making request: GET /not-found");
114 let response = client.get("/not-found").send();
115 println!(
116 "GET /not-found -> {} {}",
117 response.status().as_u16(),
118 response.status().canonical_reason()
119 );
120 if !check_eq(
121 response.status().as_u16(),
122 404,
123 "Unknown routes should return 404",
124 ) {
125 return;
126 }
127
128 println!("\nAll assertions passed!");
129}More examples
examples/getting_started.rs (line 50)
34fn main() {
35 println!("Getting Started Guide - Code Validation\n");
36
37 // === Basic App Example ===
38 println!("1. Basic app with two routes:");
39 let app = App::builder()
40 .get("/", hello)
41 .get("/health", health)
42 .build();
43
44 println!(" Routes: {}", app.route_count());
45 let client = TestClient::new(app);
46
47 let response = client.get("/").send();
48 println!(
49 " GET / -> {} ({})",
50 response.status().as_u16(),
51 response.text()
52 );
53 if !check_eq(response.status().as_u16(), 200, "GET / should return 200") {
54 return;
55 }
56 if !check_eq(response.text(), "Hello, World!", "GET / should return body") {
57 return;
58 }
59
60 let response = client.get("/health").send();
61 println!(
62 " GET /health -> {} ({})",
63 response.status().as_u16(),
64 response.text()
65 );
66 if !check_eq(
67 response.status().as_u16(),
68 200,
69 "GET /health should return 200",
70 ) {
71 return;
72 }
73
74 // === App with Middleware ===
75 println!("\n2. App with middleware:");
76 let app = App::builder()
77 .middleware(RequestIdMiddleware::new())
78 .middleware(SecurityHeaders::new())
79 .get("/", hello)
80 .build();
81
82 let client = TestClient::new(app);
83 let response = client.get("/").send();
84 println!(" GET / -> {}", response.status().as_u16());
85 if !check_eq(
86 response.status().as_u16(),
87 200,
88 "GET / with middleware should return 200",
89 ) {
90 return;
91 }
92
93 // === App with Configuration ===
94 println!("\n3. App with configuration:");
95 let config = AppConfig::new()
96 .name("My API")
97 .version("1.0.0")
98 .debug(true)
99 .max_body_size(10 * 1024 * 1024)
100 .request_timeout_ms(30_000);
101
102 let app = App::builder().config(config).get("/", hello).build();
103
104 println!(" App name: {}", app.config().name);
105 println!(" Version: {}", app.config().version);
106 if !check_eq(
107 app.config().name.as_str(),
108 "My API",
109 "Config name should match",
110 ) {
111 return;
112 }
113 if !check_eq(
114 app.config().version.as_str(),
115 "1.0.0",
116 "Config version should match",
117 ) {
118 return;
119 }
120
121 // === 404 for unknown routes ===
122 println!("\n4. 404 for unknown routes:");
123 let app = App::builder().get("/", hello).build();
124
125 let client = TestClient::new(app);
126 let response = client.get("/nonexistent").send();
127 println!(" GET /nonexistent -> {}", response.status().as_u16());
128 if !check_eq(
129 response.status().as_u16(),
130 404,
131 "Unknown routes should return 404",
132 ) {
133 return;
134 }
135
136 println!("\nAll getting started examples validated successfully!");
137}examples/crud_api.rs (line 304)
271fn main() {
272 // Initialize the global store
273 let mut guard = STORE
274 .lock()
275 .unwrap_or_else(std::sync::PoisonError::into_inner);
276 *guard = Some(UserDb {
277 users: HashMap::new(),
278 next_id: 1,
279 });
280
281 println!("fastapi_rust CRUD API Example");
282 println!("=============================\n");
283
284 let app = App::builder()
285 .post("/users", create_user)
286 .get("/users", list_users)
287 .get("/users/{id}", get_user)
288 .put("/users/{id}", update_user)
289 .delete("/users/{id}", delete_user)
290 .build();
291
292 println!("App created with {} route(s)\n", app.route_count());
293 let client = TestClient::new(app);
294
295 // 1. Create users
296 println!("1. Create users");
297 let resp = client
298 .post("/users")
299 .header("content-type", "application/json")
300 .body(r#"{"name": "Alice", "email": "alice@example.com"}"#)
301 .send();
302 println!(
303 " POST /users -> {} {}",
304 resp.status().as_u16(),
305 resp.text()
306 );
307 if !check_eq(resp.status().as_u16(), 201, "Create user should return 201") {
308 return;
309 }
310
311 let resp = client
312 .post("/users")
313 .header("content-type", "application/json")
314 .body(r#"{"name": "Bob", "email": "bob@example.com"}"#)
315 .send();
316 println!(
317 " POST /users -> {} {}",
318 resp.status().as_u16(),
319 resp.text()
320 );
321 if !check_eq(resp.status().as_u16(), 201, "Create user should return 201") {
322 return;
323 }
324
325 // 2. List users
326 println!("\n2. List all users");
327 let resp = client.get("/users").send();
328 println!(
329 " GET /users -> {} {}",
330 resp.status().as_u16(),
331 resp.text()
332 );
333 if !check_eq(resp.status().as_u16(), 200, "List users should return 200") {
334 return;
335 }
336
337 // 3. Get user by ID
338 println!("\n3. Get user by ID");
339 let resp = client.get("/users/1").send();
340 println!(
341 " GET /users/1 -> {} {}",
342 resp.status().as_u16(),
343 resp.text()
344 );
345 if !check_eq(resp.status().as_u16(), 200, "Get user should return 200") {
346 return;
347 }
348
349 // 4. Get nonexistent user
350 println!("\n4. Get nonexistent user");
351 let resp = client.get("/users/999").send();
352 println!(
353 " GET /users/999 -> {} {}",
354 resp.status().as_u16(),
355 resp.text()
356 );
357 if !check_eq(
358 resp.status().as_u16(),
359 404,
360 "Missing user should return 404",
361 ) {
362 return;
363 }
364
365 // 5. Update user
366 println!("\n5. Update user");
367 let resp = client
368 .put("/users/1")
369 .header("content-type", "application/json")
370 .body(r#"{"name": "Alice Smith", "email": "alice.smith@example.com"}"#)
371 .send();
372 println!(
373 " PUT /users/1 -> {} {}",
374 resp.status().as_u16(),
375 resp.text()
376 );
377 if !check_eq(resp.status().as_u16(), 200, "Update user should return 200") {
378 return;
379 }
380
381 // 6. Validation: empty name
382 println!("\n6. Validation error (empty name)");
383 let resp = client
384 .post("/users")
385 .header("content-type", "application/json")
386 .body(r#"{"name": "", "email": "bad@example.com"}"#)
387 .send();
388 println!(
389 " POST /users -> {} {}",
390 resp.status().as_u16(),
391 resp.text()
392 );
393 if !check_eq(resp.status().as_u16(), 400, "Empty name should return 400") {
394 return;
395 }
396
397 // 7. Validation: invalid email
398 println!("\n7. Validation error (invalid email)");
399 let resp = client
400 .post("/users")
401 .header("content-type", "application/json")
402 .body(r#"{"name": "Charlie", "email": "not-an-email"}"#)
403 .send();
404 println!(
405 " POST /users -> {} {}",
406 resp.status().as_u16(),
407 resp.text()
408 );
409 if !check_eq(
410 resp.status().as_u16(),
411 400,
412 "Invalid email should return 400",
413 ) {
414 return;
415 }
416
417 // 8. Wrong Content-Type
418 println!("\n8. Wrong Content-Type");
419 let resp = client
420 .post("/users")
421 .header("content-type", "text/plain")
422 .body(r#"{"name": "Dan", "email": "dan@example.com"}"#)
423 .send();
424 println!(
425 " POST /users -> {} {}",
426 resp.status().as_u16(),
427 resp.text()
428 );
429 if !check_eq(
430 resp.status().as_u16(),
431 415,
432 "Wrong Content-Type should return 415",
433 ) {
434 return;
435 }
436
437 // 9. Delete user
438 println!("\n9. Delete user");
439 let resp = client.delete("/users/2").send();
440 println!(" DELETE /users/2 -> {}", resp.status().as_u16());
441 if !check_eq(resp.status().as_u16(), 204, "Delete user should return 204") {
442 return;
443 }
444
445 // 10. Verify deletion
446 println!("\n10. Verify deletion");
447 let resp = client.get("/users/2").send();
448 println!(
449 " GET /users/2 -> {} {}",
450 resp.status().as_u16(),
451 resp.text()
452 );
453 if !check_eq(
454 resp.status().as_u16(),
455 404,
456 "Deleted user should return 404",
457 ) {
458 return;
459 }
460
461 let resp = client.get("/users").send();
462 println!(
463 " GET /users -> {} {}",
464 resp.status().as_u16(),
465 resp.text()
466 );
467 if !check_eq(resp.status().as_u16(), 200, "List users should return 200") {
468 return;
469 }
470
471 println!("\nAll CRUD operations passed!");
472}examples/auth_example.rs (line 293)
267fn main() {
268 println!("fastapi_rust Authentication Example");
269 println!("====================================\n");
270
271 // Build the application with public and protected routes
272 let app = App::builder()
273 // Public endpoints - accessible to everyone
274 .get("/public", public_handler)
275 // Login endpoint - returns a token
276 .post("/login", login_handler)
277 // Protected endpoint - requires valid bearer token
278 .get("/protected", protected_handler)
279 .build();
280
281 println!("App created with {} route(s)\n", app.route_count());
282
283 // Create a test client
284 let client = TestClient::new(app);
285
286 // =========================================================================
287 // Test 1: Public endpoint - no auth required
288 // =========================================================================
289 println!("1. Public endpoint - no auth required");
290 let response = client.get("/public").send();
291 println!(
292 " GET /public -> {} {}",
293 response.status().as_u16(),
294 response.status().canonical_reason()
295 );
296 if !check_eq(
297 response.status().as_u16(),
298 200,
299 "GET /public should return 200",
300 ) {
301 return;
302 }
303 if !check(
304 response.text().contains("public endpoint"),
305 "GET /public should include the public endpoint body",
306 ) {
307 return;
308 }
309
310 // =========================================================================
311 // Test 2: Protected endpoint - without token (should get 401)
312 // =========================================================================
313 println!("\n2. Protected endpoint - without token");
314 let response = client.get("/protected").send();
315 println!(
316 " GET /protected -> {} {}",
317 response.status().as_u16(),
318 response.status().canonical_reason()
319 );
320 if !check_eq(
321 response.status().as_u16(),
322 401,
323 "Protected endpoint should return 401 without token",
324 ) {
325 return;
326 }
327
328 // Check for WWW-Authenticate header
329 let has_www_auth = response
330 .headers()
331 .iter()
332 .any(|(name, value)| name == "www-authenticate" && value == b"Bearer");
333 if !check(
334 has_www_auth,
335 "401 response should include WWW-Authenticate: Bearer header",
336 ) {
337 return;
338 }
339
340 // =========================================================================
341 // Test 3: Login endpoint - get a token
342 // =========================================================================
343 println!("\n3. Login endpoint - get a token");
344 let response = client
345 .post("/login")
346 .header("content-type", "application/json")
347 .body(r#"{"username":"test","password":"test123"}"#)
348 .send();
349 println!(
350 " POST /login -> {} {}",
351 response.status().as_u16(),
352 response.status().canonical_reason()
353 );
354 if !check_eq(
355 response.status().as_u16(),
356 200,
357 "POST /login should return 200",
358 ) {
359 return;
360 }
361
362 // Parse the response to get the token
363 let body_text = response.text();
364 let body: serde_json::Value = match serde_json::from_str(body_text) {
365 Ok(body) => body,
366 Err(err) => {
367 eprintln!("Failed to parse login response JSON: {err}");
368 return;
369 }
370 };
371 let Some(bearer_value) = body.get("access_token").and_then(|value| value.as_str()) else {
372 eprintln!("Login response missing access_token");
373 return;
374 };
375 println!(" Bearer value: {bearer_value}");
376 if !check_eq(
377 bearer_value,
378 DEMO_BEARER_VALUE,
379 "Login should return the expected bearer value",
380 ) {
381 return;
382 }
383
384 // =========================================================================
385 // Test 4: Protected endpoint - with valid token (should get 200)
386 // =========================================================================
387 println!("\n4. Protected endpoint - with valid token");
388 let response = client
389 .get("/protected")
390 .header("authorization", format!("Bearer {DEMO_BEARER_VALUE}"))
391 .send();
392 println!(
393 " GET /protected (Authorization: Bearer {}) -> {} {}",
394 DEMO_BEARER_VALUE,
395 response.status().as_u16(),
396 response.status().canonical_reason()
397 );
398 if !check_eq(
399 response.status().as_u16(),
400 200,
401 "Protected endpoint should return 200 with valid token",
402 ) {
403 return;
404 }
405 if !check(
406 response.text().contains("protected resource"),
407 "Protected endpoint should include protected resource body",
408 ) {
409 return;
410 }
411
412 // =========================================================================
413 // Test 5: Protected endpoint - with invalid token (should get 403)
414 // =========================================================================
415 println!("\n5. Protected endpoint - with invalid token");
416 let response = client
417 .get("/protected")
418 .header("authorization", "Bearer wrong_token")
419 .send();
420 println!(
421 " GET /protected (Authorization: Bearer wrong_token) -> {} {}",
422 response.status().as_u16(),
423 response.status().canonical_reason()
424 );
425 if !check_eq(
426 response.status().as_u16(),
427 403,
428 "Protected endpoint should return 403 with invalid token",
429 ) {
430 return;
431 }
432
433 // =========================================================================
434 // Test 6: Protected endpoint - with wrong auth scheme (should get 401)
435 // =========================================================================
436 println!("\n6. Protected endpoint - with wrong auth scheme");
437 let response = client
438 .get("/protected")
439 .header("authorization", "Basic dXNlcjpwYXNz")
440 .send();
441 println!(
442 " GET /protected (Authorization: Basic ...) -> {} {}",
443 response.status().as_u16(),
444 response.status().canonical_reason()
445 );
446 if !check_eq(
447 response.status().as_u16(),
448 401,
449 "Protected endpoint should return 401 with wrong auth scheme",
450 ) {
451 return;
452 }
453
454 // =========================================================================
455 // Test 7: Login with wrong Content-Type (should get 415)
456 // =========================================================================
457 println!("\n7. Login with wrong Content-Type");
458 let response = client
459 .post("/login")
460 .header("content-type", "text/plain")
461 .body("demo=true")
462 .send();
463 println!(
464 " POST /login (Content-Type: text/plain) -> {} {}",
465 response.status().as_u16(),
466 response.status().canonical_reason()
467 );
468 if !check_eq(
469 response.status().as_u16(),
470 415,
471 "Login should return 415 with wrong Content-Type",
472 ) {
473 return;
474 }
475
476 // =========================================================================
477 // Test 8: Token case sensitivity (lowercase 'bearer')
478 // =========================================================================
479 println!("\n8. Token case sensitivity (lowercase 'bearer')");
480 let response = client
481 .get("/protected")
482 .header("authorization", format!("bearer {DEMO_BEARER_VALUE}"))
483 .send();
484 println!(
485 " GET /protected (Authorization: bearer {}) -> {} {}",
486 DEMO_BEARER_VALUE,
487 response.status().as_u16(),
488 response.status().canonical_reason()
489 );
490 if !check_eq(
491 response.status().as_u16(),
492 200,
493 "Bearer scheme should be case-insensitive (lowercase accepted)",
494 ) {
495 return;
496 }
497
498 println!("\nAll authentication tests passed!");
499}Sourcepub const fn allows_body(self) -> bool
pub const fn allows_body(self) -> bool
Check if status code allows a body.
Sourcepub const fn canonical_reason(self) -> &'static str
pub const fn canonical_reason(self) -> &'static str
Get the canonical reason phrase.
Examples found in repository?
examples/hello_world.rs (line 100)
61fn main() {
62 println!("fastapi_rust Hello World Example");
63 println!("================================\n");
64
65 // Build the application
66 //
67 // App::builder() creates a new application builder that lets you:
68 // - Add routes for different HTTP methods
69 // - Configure middleware
70 // - Set application state
71 // - Define exception handlers
72 let app = App::builder()
73 // Register a GET handler for the root path "/"
74 //
75 // This is equivalent to:
76 // @app.get("/")
77 // def hello():
78 // return "Hello, World!"
79 // in Python FastAPI
80 .get("/", hello_handler)
81 // Build the final immutable App
82 .build();
83
84 println!("App created with {} route(s)\n", app.route_count());
85
86 // Create a test client to make requests to our app
87 //
88 // TestClient wraps any Handler (including App) and provides
89 // a convenient API for making HTTP requests in tests.
90 let client = TestClient::new(app);
91
92 // Make a GET request to "/"
93 println!("Making request: GET /");
94 let response = client.get("/").send();
95
96 // Check the response
97 println!(
98 "GET / -> {} {}",
99 response.status().as_u16(),
100 response.status().canonical_reason()
101 );
102 println!("Response: {}\n", response.text());
103
104 // Verify success
105 if !check_eq(response.status().as_u16(), 200, "GET / should return 200") {
106 return;
107 }
108 if !check_eq(response.text(), "Hello, World!", "GET / should return body") {
109 return;
110 }
111
112 // Try a path that doesn't exist - should get 404
113 println!("Making request: GET /not-found");
114 let response = client.get("/not-found").send();
115 println!(
116 "GET /not-found -> {} {}",
117 response.status().as_u16(),
118 response.status().canonical_reason()
119 );
120 if !check_eq(
121 response.status().as_u16(),
122 404,
123 "Unknown routes should return 404",
124 ) {
125 return;
126 }
127
128 println!("\nAll assertions passed!");
129}More examples
examples/auth_example.rs (line 294)
267fn main() {
268 println!("fastapi_rust Authentication Example");
269 println!("====================================\n");
270
271 // Build the application with public and protected routes
272 let app = App::builder()
273 // Public endpoints - accessible to everyone
274 .get("/public", public_handler)
275 // Login endpoint - returns a token
276 .post("/login", login_handler)
277 // Protected endpoint - requires valid bearer token
278 .get("/protected", protected_handler)
279 .build();
280
281 println!("App created with {} route(s)\n", app.route_count());
282
283 // Create a test client
284 let client = TestClient::new(app);
285
286 // =========================================================================
287 // Test 1: Public endpoint - no auth required
288 // =========================================================================
289 println!("1. Public endpoint - no auth required");
290 let response = client.get("/public").send();
291 println!(
292 " GET /public -> {} {}",
293 response.status().as_u16(),
294 response.status().canonical_reason()
295 );
296 if !check_eq(
297 response.status().as_u16(),
298 200,
299 "GET /public should return 200",
300 ) {
301 return;
302 }
303 if !check(
304 response.text().contains("public endpoint"),
305 "GET /public should include the public endpoint body",
306 ) {
307 return;
308 }
309
310 // =========================================================================
311 // Test 2: Protected endpoint - without token (should get 401)
312 // =========================================================================
313 println!("\n2. Protected endpoint - without token");
314 let response = client.get("/protected").send();
315 println!(
316 " GET /protected -> {} {}",
317 response.status().as_u16(),
318 response.status().canonical_reason()
319 );
320 if !check_eq(
321 response.status().as_u16(),
322 401,
323 "Protected endpoint should return 401 without token",
324 ) {
325 return;
326 }
327
328 // Check for WWW-Authenticate header
329 let has_www_auth = response
330 .headers()
331 .iter()
332 .any(|(name, value)| name == "www-authenticate" && value == b"Bearer");
333 if !check(
334 has_www_auth,
335 "401 response should include WWW-Authenticate: Bearer header",
336 ) {
337 return;
338 }
339
340 // =========================================================================
341 // Test 3: Login endpoint - get a token
342 // =========================================================================
343 println!("\n3. Login endpoint - get a token");
344 let response = client
345 .post("/login")
346 .header("content-type", "application/json")
347 .body(r#"{"username":"test","password":"test123"}"#)
348 .send();
349 println!(
350 " POST /login -> {} {}",
351 response.status().as_u16(),
352 response.status().canonical_reason()
353 );
354 if !check_eq(
355 response.status().as_u16(),
356 200,
357 "POST /login should return 200",
358 ) {
359 return;
360 }
361
362 // Parse the response to get the token
363 let body_text = response.text();
364 let body: serde_json::Value = match serde_json::from_str(body_text) {
365 Ok(body) => body,
366 Err(err) => {
367 eprintln!("Failed to parse login response JSON: {err}");
368 return;
369 }
370 };
371 let Some(bearer_value) = body.get("access_token").and_then(|value| value.as_str()) else {
372 eprintln!("Login response missing access_token");
373 return;
374 };
375 println!(" Bearer value: {bearer_value}");
376 if !check_eq(
377 bearer_value,
378 DEMO_BEARER_VALUE,
379 "Login should return the expected bearer value",
380 ) {
381 return;
382 }
383
384 // =========================================================================
385 // Test 4: Protected endpoint - with valid token (should get 200)
386 // =========================================================================
387 println!("\n4. Protected endpoint - with valid token");
388 let response = client
389 .get("/protected")
390 .header("authorization", format!("Bearer {DEMO_BEARER_VALUE}"))
391 .send();
392 println!(
393 " GET /protected (Authorization: Bearer {}) -> {} {}",
394 DEMO_BEARER_VALUE,
395 response.status().as_u16(),
396 response.status().canonical_reason()
397 );
398 if !check_eq(
399 response.status().as_u16(),
400 200,
401 "Protected endpoint should return 200 with valid token",
402 ) {
403 return;
404 }
405 if !check(
406 response.text().contains("protected resource"),
407 "Protected endpoint should include protected resource body",
408 ) {
409 return;
410 }
411
412 // =========================================================================
413 // Test 5: Protected endpoint - with invalid token (should get 403)
414 // =========================================================================
415 println!("\n5. Protected endpoint - with invalid token");
416 let response = client
417 .get("/protected")
418 .header("authorization", "Bearer wrong_token")
419 .send();
420 println!(
421 " GET /protected (Authorization: Bearer wrong_token) -> {} {}",
422 response.status().as_u16(),
423 response.status().canonical_reason()
424 );
425 if !check_eq(
426 response.status().as_u16(),
427 403,
428 "Protected endpoint should return 403 with invalid token",
429 ) {
430 return;
431 }
432
433 // =========================================================================
434 // Test 6: Protected endpoint - with wrong auth scheme (should get 401)
435 // =========================================================================
436 println!("\n6. Protected endpoint - with wrong auth scheme");
437 let response = client
438 .get("/protected")
439 .header("authorization", "Basic dXNlcjpwYXNz")
440 .send();
441 println!(
442 " GET /protected (Authorization: Basic ...) -> {} {}",
443 response.status().as_u16(),
444 response.status().canonical_reason()
445 );
446 if !check_eq(
447 response.status().as_u16(),
448 401,
449 "Protected endpoint should return 401 with wrong auth scheme",
450 ) {
451 return;
452 }
453
454 // =========================================================================
455 // Test 7: Login with wrong Content-Type (should get 415)
456 // =========================================================================
457 println!("\n7. Login with wrong Content-Type");
458 let response = client
459 .post("/login")
460 .header("content-type", "text/plain")
461 .body("demo=true")
462 .send();
463 println!(
464 " POST /login (Content-Type: text/plain) -> {} {}",
465 response.status().as_u16(),
466 response.status().canonical_reason()
467 );
468 if !check_eq(
469 response.status().as_u16(),
470 415,
471 "Login should return 415 with wrong Content-Type",
472 ) {
473 return;
474 }
475
476 // =========================================================================
477 // Test 8: Token case sensitivity (lowercase 'bearer')
478 // =========================================================================
479 println!("\n8. Token case sensitivity (lowercase 'bearer')");
480 let response = client
481 .get("/protected")
482 .header("authorization", format!("bearer {DEMO_BEARER_VALUE}"))
483 .send();
484 println!(
485 " GET /protected (Authorization: bearer {}) -> {} {}",
486 DEMO_BEARER_VALUE,
487 response.status().as_u16(),
488 response.status().canonical_reason()
489 );
490 if !check_eq(
491 response.status().as_u16(),
492 200,
493 "Bearer scheme should be case-insensitive (lowercase accepted)",
494 ) {
495 return;
496 }
497
498 println!("\nAll authentication tests passed!");
499}Trait Implementations§
Source§impl Clone for StatusCode
impl Clone for StatusCode
Source§fn clone(&self) -> StatusCode
fn clone(&self) -> StatusCode
Returns a duplicate of the value. Read more
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreSource§impl Debug for StatusCode
impl Debug for StatusCode
Source§impl Hash for StatusCode
impl Hash for StatusCode
Source§impl IntoStatusU16 for StatusCode
impl IntoStatusU16 for StatusCode
fn into_status_u16(self) -> u16
Source§impl PartialEq for StatusCode
impl PartialEq for StatusCode
impl Copy for StatusCode
impl Eq for StatusCode
impl StructuralPartialEq for StatusCode
Auto Trait Implementations§
impl Freeze for StatusCode
impl RefUnwindSafe for StatusCode
impl Send for StatusCode
impl Sync for StatusCode
impl Unpin for StatusCode
impl UnwindSafe for StatusCode
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, _span: NoopSpan) -> Self
fn instrument(self, _span: NoopSpan) -> Self
Instruments this future with a span (no-op when disabled).
Source§fn in_current_span(self) -> Self
fn in_current_span(self) -> Self
Instruments this future with the current span (no-op when disabled).