Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Message::addHeader() to add header with the same name #8194

Merged
merged 6 commits into from
Nov 20, 2023

Conversation

kenjis
Copy link
Member

@kenjis kenjis commented Nov 12, 2023

Description
For #5041

  • add Message::addHeader()
  • implement Message::header() "return an array of header objects":
    * Returns a single Header object. If multiple headers with the same
    * name exist, then will return an array of header objects.

Checklist:

  • Securely signed commits
  • Component(s) with PHPDoc blocks, only if necessary or adds value
  • Unit testing, with >80% coverage
  • User guide updated
  • Conforms to style guide

@kenjis kenjis added enhancement PRs that improve existing functionalities 4.5 docs needed Pull requests needing documentation write-ups and/or revisions. labels Nov 12, 2023
@kenjis kenjis changed the title feat: add Message::addHeader() to add header with the same namee feat: add Message::addHeader() to add header with the same name Nov 12, 2023
@kenjis kenjis force-pushed the feat-Message-addHeader branch from 4059b72 to 5c5675b Compare November 12, 2023 01:15
@kenjis kenjis force-pushed the feat-Message-addHeader branch 2 times, most recently from c60bb05 to 8af1e3d Compare November 12, 2023 02:41
Copy link
Member

@michalsn michalsn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get it. Multiple headers with the same name should be handled with a single Header class that has multiple values.

Am I missing something?

@kenjis
Copy link
Member Author

kenjis commented Nov 13, 2023

The question is not strange. It seems PSR-7 specification is like that.

But I don't think these are the same:

  1. a header with multiple values
  2. multiple headers with the same name

If we handle multiple headers with the same name with a single Header class that has multiple values,
we cannot distinguish them.

If we were to send three Set-Cookie headers, how would we handle them?
We need to know that in the case of Set-Cookies, one value is sent with one header.

@michalsn
Copy link
Member

The main issue I have with this is that code like this:

$this->request->header('NAME')?->getValueLine();

is no longer guaranteed to work.

If we handle multiple headers with the same name with a single Header class that has multiple values,
we cannot distinguish them.

I don't see much difference when it comes to distinguishing between values, because in both cases we are dealing with a simple array. There are no methods that make it easy to work with multiple objects of the Header class. On the other hand, there are plenty that make it easier to work with multiple values in the Header class.

As you said, the PSR-7 seems to implement it just like we do right now: https://www.php-fig.org/psr/psr-7/#headers-with-multiple-values

Note: Not all header values can be concatenated using a comma (e.g., Set-Cookie). When working with such headers, consumers of MessageInterface-based classes SHOULD rely on the getHeader() method for retrieving such multi-valued headers.

In our case, it would be:

$this->request->header('NAME')?->getValue();

@kenjis
Copy link
Member Author

kenjis commented Nov 14, 2023

If $this->request->header('NAME')?->getValueLine() works now, it will work.

        $this->message->setHeader('accept', ['json', 'html']);
        $this->message->appendHeader('Accept', 'xml');
        dd($this->message->header('accept')?->getValueLine());
        // "json, html, xml"

But the following code does not make sense, because we cannot use logged_in=no; Path=/, sessid=123456; Path=/.

        $this->message->appendHeader(
            'Set-Cookie',
            'logged_in=no; Path=/'
        );
        $this->message->appendHeader(
            'Set-Cookie',
            'sessid=123456; Path=/'
        );
        dd($this->message->header('Set-Cookie')?->getValueLine());
        // string (43) "logged_in=no; Path=/, sessid=123456; Path=/"

In PSR-7, we also cannot use getHeaderLine() as you pointed out.

require __DIR__ . '/vendor/autoload.php';

$response = new \Laminas\Diactoros\Response();

$cookie1 = 'user_id=123; Path=/; Expires='
    . gmdate('D, d M Y H:i:s T', strtotime('+1 day'));
$cookie2 = 'session_token=abc123; Path=/; Secure; HttpOnly; SameSite=None; Expires='
    . gmdate('D, d M Y H:i:s T', strtotime('+7 days'));

$response = $response->withAddedHeader('Set-Cookie', $cookie1);
$response = $response->withAddedHeader('Set-Cookie', $cookie2);

var_dump($response->getHeader('Set-Cookie'));
var_dump($response->getHeaderLine('Set-Cookie'));
$ php test.php 
array(2) {
  [0] =>
  string(58) "user_id=123; Path=/; Expires=Wed, 15 Nov 2023 05:09:03 GMT"
  [1] =>
  string(100) "session_token=abc123; Path=/; Secure; HttpOnly; SameSite=None; Expires=Tue, 21 Nov 2023 05:09:03 GMT"
}
string(159) "user_id=123; Path=/; Expires=Wed, 15 Nov 2023 05:09:03 GMT,session_token=abc123; Path=/; Secure; HttpOnly; SameSite=None; Expires=Tue, 21 Nov 2023 05:09:03 GMT"

@kenjis
Copy link
Member Author

kenjis commented Nov 14, 2023

After all, our question is:
How do we get and forward all headers with CURLRequest (and Response)?
See #5041

@michalsn
Copy link
Member

michalsn commented Nov 14, 2023

Yes... you are right. I clearly missed a lot. There is no other way around it.

However, I still see some issues.

If $this->request->header('NAME')?->getValueLine() works now, it will work.

And that's the problem. If we call:

$this->request->addHeader('NAME', 'value1');
$this->request->addHeader('NAME', 'value2');

then it will not work. We will receive an error when we try to use it. This should be at least documented in the changelog. Or even better handled just like you showed in Laminas.

// Edit
The very last sentence obviously applies to the $this->request->getHeaderLine('NAME').

@kenjis kenjis force-pushed the feat-Message-addHeader branch from 8af1e3d to b89f51e Compare November 14, 2023 22:01
@kenjis
Copy link
Member Author

kenjis commented Nov 14, 2023

Thanks. To get multiple headers and make them into one line does not make sense.
So it should cause an exception. I did so.

@michalsn
Copy link
Member

michalsn commented Nov 15, 2023

Hopefully, others can wave in with a comment about this.
Seems like only a changelog/docs is missing.

@kenjis kenjis requested a review from MGatner November 15, 2023 06:09
Copy link
Member

@MGatner MGatner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the thread around the confusion and I think this is probably the best that we can do. I'm not entirely sure PSR-7 handles this correctly but that's our guide.

@kenjis
Copy link
Member Author

kenjis commented Nov 19, 2023

PSR-7 cannot handle multiple headers with the same name correctly.

@kenjis kenjis force-pushed the feat-Message-addHeader branch from b89f51e to 4c55a28 Compare November 19, 2023 21:18
@kenjis kenjis removed the docs needed Pull requests needing documentation write-ups and/or revisions. label Nov 19, 2023
@kenjis
Copy link
Member Author

kenjis commented Nov 19, 2023

Rebased and added docs.

@kenjis kenjis force-pushed the feat-Message-addHeader branch from 989167f to 5d55cd0 Compare November 20, 2023 07:23
@kenjis kenjis merged commit 9954ccf into codeigniter4:4.5 Nov 20, 2023
46 checks passed
@kenjis kenjis deleted the feat-Message-addHeader branch November 20, 2023 21:22
@kenjis kenjis mentioned this pull request Nov 21, 2023
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
4.5 enhancement PRs that improve existing functionalities
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants