|
29 | 29 |
|
30 | 30 | sys.path.append("../common") |
31 | 31 |
|
| 32 | +import gzip |
| 33 | +import io |
32 | 34 | import json |
33 | 35 | import unittest |
34 | 36 |
|
@@ -58,7 +60,7 @@ class InferSizeLimitTest(tu.TestResultCollector): |
58 | 60 | def _get_infer_url(self, model_name): |
59 | 61 | return "http://localhost:8000/v2/models/{}/infer".format(model_name) |
60 | 62 |
|
61 | | - def test_default_limit_rejection_raw_binary(self): |
| 63 | + def test_default_limit_raw_binary(self): |
62 | 64 | """Test raw binary inputs with default limit""" |
63 | 65 | model = "onnx_zero_1_float32" |
64 | 66 |
|
@@ -124,7 +126,7 @@ def test_default_limit_rejection_raw_binary(self): |
124 | 126 | "Response data does not match input data", |
125 | 127 | ) |
126 | 128 |
|
127 | | - def test_default_limit_rejection_json(self): |
| 129 | + def test_default_limit_json(self): |
128 | 130 | """Test JSON inputs with default limit""" |
129 | 131 | model = "onnx_zero_1_float32" |
130 | 132 |
|
@@ -415,6 +417,193 @@ def test_large_string_in_json(self): |
415 | 417 | error_msg, |
416 | 418 | ) |
417 | 419 |
|
| 420 | + def _create_compressed_payload(self, target_size): |
| 421 | + """Helper to create a gzip-compressed JSON payload of specified decompressed size.""" |
| 422 | + shape_size = 1000 # Small actual data |
| 423 | + payload = { |
| 424 | + "inputs": [ |
| 425 | + { |
| 426 | + "name": "INPUT0", |
| 427 | + "datatype": "FP32", |
| 428 | + "shape": [1, shape_size], |
| 429 | + "data": [1.0] * shape_size, |
| 430 | + } |
| 431 | + ] |
| 432 | + } |
| 433 | + json_str = json.dumps(payload, indent=4) |
| 434 | + |
| 435 | + # Pad with whitespace to reach target size (whitespace before closing brace is valid JSON) |
| 436 | + padding_needed = target_size - len(json_str) |
| 437 | + padded_json = json_str[:-1] + (" " * padding_needed) + json_str[-1] |
| 438 | + |
| 439 | + # Compress the payload |
| 440 | + compressed_buffer = io.BytesIO() |
| 441 | + with gzip.GzipFile(fileobj=compressed_buffer, mode="wb") as gz: |
| 442 | + gz.write(padded_json.encode("utf-8")) |
| 443 | + |
| 444 | + return compressed_buffer.getvalue(), len(padded_json.encode("utf-8")) |
| 445 | + |
| 446 | + def test_default_limit_compressed(self): |
| 447 | + """Test compressed inputs with default 64MB limit. |
| 448 | +
|
| 449 | + This test verifies that the --http-max-input-size limit is enforced on |
| 450 | + the decompressed data size, not just the compressed request size. |
| 451 | + """ |
| 452 | + model = "onnx_zero_1_float32" |
| 453 | + |
| 454 | + headers = { |
| 455 | + "Content-Type": "application/json", |
| 456 | + "Content-Encoding": "gzip", |
| 457 | + } |
| 458 | + |
| 459 | + # Test case 1: Payload that decompresses to 64MB + 1MB (over limit) should fail |
| 460 | + large_target_size = DEFAULT_LIMIT_BYTES + MB |
| 461 | + ( |
| 462 | + large_compressed_data, |
| 463 | + large_uncompressed_size, |
| 464 | + ) = self._create_compressed_payload(large_target_size) |
| 465 | + |
| 466 | + # Verify uncompressed size is over 64MB limit |
| 467 | + self.assertGreater( |
| 468 | + large_uncompressed_size, |
| 469 | + DEFAULT_LIMIT_BYTES, |
| 470 | + f"Large payload should decompress to > 64MB, got {large_uncompressed_size}", |
| 471 | + ) |
| 472 | + |
| 473 | + # Verify compressed size is under the limit |
| 474 | + self.assertLess( |
| 475 | + len(large_compressed_data), |
| 476 | + DEFAULT_LIMIT_BYTES, |
| 477 | + f"Compressed size should be under limit, got {len(large_compressed_data)}", |
| 478 | + ) |
| 479 | + |
| 480 | + response = requests.post( |
| 481 | + self._get_infer_url(model), data=large_compressed_data, headers=headers |
| 482 | + ) |
| 483 | + |
| 484 | + # Should fail with 400 bad request - decompressed size exceeds limit |
| 485 | + self.assertEqual( |
| 486 | + 400, |
| 487 | + response.status_code, |
| 488 | + f"Expected 400 for compressed request that decompresses to >64MB, got: {response.status_code}", |
| 489 | + ) |
| 490 | + |
| 491 | + # Verify error message contains size limit info |
| 492 | + error_msg = response.content.decode() |
| 493 | + self.assertIn( |
| 494 | + "exceeds the maximum allowed value", |
| 495 | + error_msg, |
| 496 | + "Expected error message about exceeding max input size", |
| 497 | + ) |
| 498 | + |
| 499 | + # Test case 2: Payload that decompresses to 64MB - 1MB (under limit) should succeed |
| 500 | + small_target_size = DEFAULT_LIMIT_BYTES - MB |
| 501 | + ( |
| 502 | + small_compressed_data, |
| 503 | + small_uncompressed_size, |
| 504 | + ) = self._create_compressed_payload(small_target_size) |
| 505 | + |
| 506 | + # Verify uncompressed size is under 64MB limit |
| 507 | + self.assertLess( |
| 508 | + small_uncompressed_size, |
| 509 | + DEFAULT_LIMIT_BYTES, |
| 510 | + f"Small payload should decompress to < 64MB, got {small_uncompressed_size}", |
| 511 | + ) |
| 512 | + |
| 513 | + response = requests.post( |
| 514 | + self._get_infer_url(model), data=small_compressed_data, headers=headers |
| 515 | + ) |
| 516 | + |
| 517 | + # Should succeed with 200 OK |
| 518 | + self.assertEqual( |
| 519 | + 200, |
| 520 | + response.status_code, |
| 521 | + f"Expected 200 for compressed request within limit, got: {response.status_code}", |
| 522 | + ) |
| 523 | + |
| 524 | + # Verify we got a valid response |
| 525 | + result = response.json() |
| 526 | + self.assertIn("outputs", result, "Response missing outputs field") |
| 527 | + |
| 528 | + def test_large_input_compressed(self): |
| 529 | + """Test compressed inputs with custom 128MB limit set. |
| 530 | +
|
| 531 | + This test verifies that compressed inputs work correctly when the |
| 532 | + --http-max-input-size limit is increased. |
| 533 | + """ |
| 534 | + model = "onnx_zero_1_float32" |
| 535 | + |
| 536 | + headers = { |
| 537 | + "Content-Type": "application/json", |
| 538 | + "Content-Encoding": "gzip", |
| 539 | + } |
| 540 | + |
| 541 | + # Test case 1: Input that decompresses to 128MB + 1MB (over limit) should fail |
| 542 | + large_target_size = INCREASED_LIMIT_BYTES + MB |
| 543 | + ( |
| 544 | + large_compressed_data, |
| 545 | + large_uncompressed_size, |
| 546 | + ) = self._create_compressed_payload(large_target_size) |
| 547 | + |
| 548 | + # Verify sizes |
| 549 | + self.assertGreater( |
| 550 | + large_uncompressed_size, |
| 551 | + INCREASED_LIMIT_BYTES, |
| 552 | + f"Large payload should decompress to > 128MB, got {large_uncompressed_size}", |
| 553 | + ) |
| 554 | + |
| 555 | + response = requests.post( |
| 556 | + self._get_infer_url(model), data=large_compressed_data, headers=headers |
| 557 | + ) |
| 558 | + |
| 559 | + # Should fail with 400 bad request |
| 560 | + self.assertEqual( |
| 561 | + 400, |
| 562 | + response.status_code, |
| 563 | + f"Expected 400 for compressed request exceeding 128MB limit, got: {response.status_code}", |
| 564 | + ) |
| 565 | + |
| 566 | + error_msg = response.content.decode() |
| 567 | + self.assertIn( |
| 568 | + "exceeds the maximum allowed value", |
| 569 | + error_msg, |
| 570 | + "Expected error message about exceeding max input size", |
| 571 | + ) |
| 572 | + |
| 573 | + # Test case 2: Input that decompresses to 128MB - 1MB (under limit) should succeed |
| 574 | + small_target_size = INCREASED_LIMIT_BYTES - MB |
| 575 | + ( |
| 576 | + small_compressed_data, |
| 577 | + small_uncompressed_size, |
| 578 | + ) = self._create_compressed_payload(small_target_size) |
| 579 | + |
| 580 | + # Verify sizes |
| 581 | + self.assertLess( |
| 582 | + small_uncompressed_size, |
| 583 | + INCREASED_LIMIT_BYTES, |
| 584 | + f"Small payload should decompress to < 128MB, got {small_uncompressed_size}", |
| 585 | + ) |
| 586 | + self.assertGreater( |
| 587 | + small_uncompressed_size, |
| 588 | + DEFAULT_LIMIT_BYTES, |
| 589 | + f"Small payload should decompress to > 64MB (default), got {small_uncompressed_size}", |
| 590 | + ) |
| 591 | + |
| 592 | + response = requests.post( |
| 593 | + self._get_infer_url(model), data=small_compressed_data, headers=headers |
| 594 | + ) |
| 595 | + |
| 596 | + # Should succeed with 200 OK |
| 597 | + self.assertEqual( |
| 598 | + 200, |
| 599 | + response.status_code, |
| 600 | + f"Expected 200 for compressed request within 128MB limit, got: {response.status_code}", |
| 601 | + ) |
| 602 | + |
| 603 | + # Verify we got a valid response |
| 604 | + result = response.json() |
| 605 | + self.assertIn("outputs", result, "Response missing outputs field") |
| 606 | + |
418 | 607 |
|
419 | 608 | if __name__ == "__main__": |
420 | 609 | unittest.main() |
0 commit comments