|
14 | 14 | ["Set-Cookie", "hello=world"], |
15 | 15 | ["Accept", "*/*"], |
16 | 16 | ["set-cookie", "foo=bar"], |
17 | | - ] |
| 17 | + ].freeze |
18 | 18 | end |
19 | 19 |
|
20 | 20 | let(:headers) {subject[fields]} |
|
293 | 293 |
|
294 | 294 | headers.add("etag", "abcd") |
295 | 295 |
|
296 | | - expect(trailer.to_h).to be == {"etag" => "abcd"} |
| 296 | + expect(trailer.to_a).to be == [["etag", "abcd"]] |
297 | 297 | end |
298 | 298 |
|
299 | 299 | it "can add trailer without explicit header" do |
300 | 300 | trailer = headers.trailer! |
301 | 301 |
|
302 | 302 | headers.add("etag", "abcd") |
303 | 303 |
|
304 | | - expect(trailer.to_h).to be == {"etag" => "abcd"} |
| 304 | + expect(trailer.to_a).to be == [["etag", "abcd"]] |
305 | 305 | end |
306 | 306 |
|
307 | 307 | with "forbidden trailers" do |
|
335 | 335 |
|
336 | 336 | cookie |
337 | 337 | set-cookie |
338 | | - |
339 | | - x-foo-bar |
340 | 338 | ] |
341 | 339 |
|
342 | 340 | forbidden_trailers.each do |key| |
343 | | - it "can't add a #{key.inspect} header in the trailer", unique: key do |
344 | | - trailer = headers.trailer! |
345 | | - headers.add(key, "example") |
346 | | - expect{headers.to_h}.to raise_exception(Protocol::HTTP::InvalidTrailerError) |
| 341 | + with "forbidden trailer #{key.inspect}", unique: key do |
| 342 | + it "can't add a #{key.inspect} header in the trailer" do |
| 343 | + trailer = headers.trailer! |
| 344 | + expect do |
| 345 | + headers.add(key, "example", trailer: true) |
| 346 | + end.to raise_exception(Protocol::HTTP::InvalidTrailerError) |
| 347 | + end |
| 348 | + |
| 349 | + it "can't add a #{key.inspect} header with trailer: true" do |
| 350 | + expect do |
| 351 | + headers.add(key, "example", trailer: true) |
| 352 | + end.to raise_exception(Protocol::HTTP::InvalidTrailerError) |
| 353 | + end |
| 354 | + end |
| 355 | + end |
| 356 | + end |
| 357 | + |
| 358 | + with "unknown trailers", unique: "unknown" do |
| 359 | + let(:headers) {subject.new} |
| 360 | + |
| 361 | + unknown_trailers = %w[ |
| 362 | + x-foo-bar |
| 363 | + grpc-status |
| 364 | + grpc-message |
| 365 | + x-custom-header |
| 366 | + ] |
| 367 | + |
| 368 | + unknown_trailers.each do |key| |
| 369 | + with "unknown trailer #{key.inspect}", unique: key do |
| 370 | + it "can add unknown header #{key.inspect} as trailer" do |
| 371 | + headers.add(key, "example", trailer: true) |
| 372 | + expect(headers).to be(:include?, key) |
| 373 | + end |
347 | 374 | end |
348 | 375 | end |
349 | 376 | end |
|
359 | 386 | ] |
360 | 387 |
|
361 | 388 | permitted_trailers.each do |key| |
362 | | - it "can add a #{key.inspect} header in the trailer", unique: key do |
363 | | - trailer = headers.trailer! |
364 | | - headers.add(key, "example") |
365 | | - expect(headers).to be(:include?, key) |
| 389 | + with "permitted trailer #{key.inspect}", unique: key do |
| 390 | + it "can add a #{key.inspect} header in the trailer" do |
| 391 | + trailer = headers.trailer! |
| 392 | + headers.add(key, "example") |
| 393 | + expect(headers).to be(:include?, key) |
| 394 | + end |
| 395 | + |
| 396 | + it "can add a #{key.inspect} header with trailer: true" do |
| 397 | + headers.add(key, "example", trailer: true) |
| 398 | + expect(headers).to be(:include?, key) |
| 399 | + end |
366 | 400 | end |
367 | 401 | end |
368 | 402 | end |
369 | 403 | end |
370 | 404 |
|
| 405 | + with "#header" do |
| 406 | + it "can enumerate all headers when there are no trailers" do |
| 407 | + result = headers.header.to_a |
| 408 | + |
| 409 | + expect(result).to be == fields |
| 410 | + end |
| 411 | + |
| 412 | + it "enumerates headers but not trailers" do |
| 413 | + headers.trailer! |
| 414 | + headers.add("etag", "abcd") |
| 415 | + headers.add("digest", "sha-256=xyz") |
| 416 | + |
| 417 | + header = headers.header.to_a |
| 418 | + |
| 419 | + # Should only include the original 5 fields, not the 2 trailers |
| 420 | + expect(header.size).to be == 5 |
| 421 | + expect(header).to be == fields |
| 422 | + end |
| 423 | + |
| 424 | + it "returns an enumerator when no block is given" do |
| 425 | + enumerator = headers.header |
| 426 | + |
| 427 | + expect(enumerator).to be_a(Enumerator) |
| 428 | + expect(enumerator.to_a).to be == fields |
| 429 | + end |
| 430 | + |
| 431 | + it "returns an enumerator that excludes trailers" do |
| 432 | + headers.trailer! |
| 433 | + headers.add("etag", "abcd") |
| 434 | + |
| 435 | + enumerator = headers.header |
| 436 | + |
| 437 | + expect(enumerator).to be_a(Enumerator) |
| 438 | + expect(enumerator.to_a.size).to be == 5 |
| 439 | + expect(enumerator.to_a).to be == [ |
| 440 | + ["Content-Type", "text/html"], |
| 441 | + ["connection", "Keep-Alive"], |
| 442 | + ["Set-Cookie", "hello=world"], |
| 443 | + ["Accept", "*/*"], |
| 444 | + ["set-cookie", "foo=bar"] |
| 445 | + ] |
| 446 | + end |
| 447 | + end |
| 448 | + |
371 | 449 | with "#trailer" do |
372 | 450 | it "can enumerate trailer" do |
373 | 451 | headers.add("trailer", "etag") |
374 | 452 | headers.trailer! |
375 | 453 | headers.add("etag", "abcd") |
376 | 454 |
|
377 | | - expect(headers.trailer.to_h).to be == {"etag" => "abcd"} |
| 455 | + expect(headers.trailer.to_a).to be == [["etag", "abcd"]] |
| 456 | + end |
| 457 | + end |
| 458 | + |
| 459 | + with "#add with trailer: keyword" do |
| 460 | + let(:headers) {subject.new} |
| 461 | + |
| 462 | + it "allows adding regular headers without trailer: true" do |
| 463 | + headers.add("content-type", "text/plain") |
| 464 | + expect(headers["content-type"]).to be == "text/plain" |
| 465 | + end |
| 466 | + |
| 467 | + it "validates trailers immediately when trailer: true" do |
| 468 | + expect do |
| 469 | + headers.add("content-type", "text/plain", trailer: true) |
| 470 | + end.to raise_exception(Protocol::HTTP::InvalidTrailerError) |
| 471 | + end |
| 472 | + |
| 473 | + it "allows permitted trailers with trailer: true" do |
| 474 | + headers.add("etag", "abcd", trailer: true) |
| 475 | + expect(headers["etag"]).to be == "abcd" |
| 476 | + end |
| 477 | + |
| 478 | + it "validates trailers without calling trailer! first" do |
| 479 | + # This should fail immediately, without needing trailer! to be called |
| 480 | + expect do |
| 481 | + headers.add("authorization", "Bearer token", trailer: true) |
| 482 | + end.to raise_exception(Protocol::HTTP::InvalidTrailerError) |
| 483 | + end |
| 484 | + |
| 485 | + it "validates trailers even when headers are not indexed" do |
| 486 | + # Add without triggering indexing |
| 487 | + expect do |
| 488 | + headers.add("host", "example.com", trailer: true) |
| 489 | + end.to raise_exception(Protocol::HTTP::InvalidTrailerError) |
| 490 | + |
| 491 | + # Ensure we haven't triggered indexing yet |
| 492 | + expect(headers.instance_variable_get(:@indexed)).to be_nil |
378 | 493 | end |
379 | 494 | end |
380 | 495 |
|
|
0 commit comments