diff --git a/src/Header/GenericHeader.php b/src/Header/GenericHeader.php index 4fc868ee55..d5f27d44b1 100644 --- a/src/Header/GenericHeader.php +++ b/src/Header/GenericHeader.php @@ -80,7 +80,7 @@ public function __construct($fieldName = null, $fieldValue = null) * * @param string $fieldName * @return GenericHeader - * @throws Exception\InvalidArgumentException( + * @throws Exception\InvalidArgumentException If the name does not match with RFC 2616 format. */ public function setFieldName($fieldName) { @@ -88,13 +88,17 @@ public function setFieldName($fieldName) throw new Exception\InvalidArgumentException('Header name must be a string'); } - // Pre-filter to normalize valid characters, change underscore to dash - $fieldName = str_replace(' ', '-', ucwords(str_replace(array('_', '-'), ' ', $fieldName))); - - // Validate what we have - if (!preg_match('/^[a-z][a-z0-9-]*$/i', $fieldName)) { + /* + * Following RFC 2616 section 4.2 + * + * message-header = field-name ":" [ field-value ] + * field-name = token + * + * @see http://tools.ietf.org/html/rfc2616#section-2.2 for token definition. + */ + if (!preg_match('/^[!#-\'*+\-\.0-9A-Z\^-z|~]+$/', $fieldName)) { throw new Exception\InvalidArgumentException( - 'Header name must start with a letter, and consist of only letters, numbers, and dashes' + 'Header name must be a valid RFC 2616 (section 4.2) field-name.' ); } diff --git a/test/Header/GenericHeaderTest.php b/test/Header/GenericHeaderTest.php new file mode 100644 index 0000000000..1ee45cb8fe --- /dev/null +++ b/test/Header/GenericHeaderTest.php @@ -0,0 +1,116 @@ +assertEquals( + $e->getMessage(), + 'Header name must be a valid RFC 2616 (section 4.2) field-name.' + ); + $this->fail('Allowed char rejected: ' . ord($name)); // For easy debug + } + } + + /** + * @param string $name + * @dataProvider invalidFieldNameChars + */ + public function testInvalidFieldName($name) + { + try { + new GenericHeader($name); + $this->fail('Invalid char allowed: ' . ord($name)); // For easy debug + } catch (InvalidArgumentException $e) { + $this->assertEquals( + $e->getMessage(), + 'Header name must be a valid RFC 2616 (section 4.2) field-name.' + ); + } + } + + /** + * Valid field name characters. + * + * @return string[] + */ + public function validFieldNameChars() + { + return array( + array('!'), + array('#'), + array('$'), + array('%'), + array('&'), + array("'"), + array('*'), + array('+'), + array('-'), + array('.'), + array('0'), // Begin numeric range + array('9'), // End numeric range + array('A'), // Begin upper range + array('Z'), // End upper range + array('^'), + array('_'), + array('`'), + array('a'), // Begin lower range + array('z'), // End lower range + array('|'), + array('~'), + ); + } + + /** + * Invalid field name characters. + * + * @return string[] + */ + public function invalidFieldNameChars() + { + return array( + array("\x00"), // Min CTL invalid character range. + array("\x1F"), // Max CTL invalid character range. + array('('), + array(')'), + array('<'), + array('>'), + array('@'), + array(','), + array(';'), + array(':'), + array('\\'), + array('"'), + array('/'), + array('['), + array(']'), + array('?'), + array('='), + array('{'), + array('}'), + array(' '), + array("\t"), + array("\x7F"), // DEL CTL invalid character. + ); + } +}