]> Kevux Git Server - koopa/commitdiff
Progress: continuing development, http headers work
authorKevin Day <thekevinday@gmail.com>
Wed, 8 Mar 2017 05:48:31 +0000 (23:48 -0600)
committerKevin Day <thekevinday@gmail.com>
Wed, 8 Mar 2017 05:48:31 +0000 (23:48 -0600)
More progress with writing HTTP header processing code.

Other minor bugfixes and changes.

common/base/classes/base_http.php
common/base/classes/base_rfc_char.php
common/base/classes/base_rfc_string.php
examples/test.php
program/reservation/index.php

index 9e7ff1bca8dd2257f752320a5ab0ed8ab0af37b0..1b87310a81a4f4063d3ab7621381e0a7972129f3 100644 (file)
@@ -19,11 +19,6 @@ require_once('common/base/classes/base_mime.php');
 /**
  * A generic class for managing the HTTP protocol.
  *
- * @todo: many of the HTTP response fields can become HTML meta header fields.
- *        add a function to return an array of HTTP attributes that can be translated into HTTP tags.
- *        no processing is to be done here, let a class that is explicitly designed for HTML process it.
- *        example, content-security-policy (https://en.wikipedia.org/wiki/Content_Security_Policy).
- *
  * @see: https://www.iana.org/assignments/message-headers/message-headers.xhtml
  * @see: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
  *
@@ -207,7 +202,7 @@ class c_base_http extends c_base_rfc_string {
   const ENCODING_GZIP     = 4; // Compression Options: -1 -> 9.
   const ENCODING_BZIP     = 5; // Compression Options: 1 -> 9.
   const ENCODING_LZO      = 6; // Compression Options: LZO1_99, LZO1A_99, LZO1B_999, LZO1C_999, LZO1F_999, LZO1X_999, LZO1Y_999, LZO1Z_999, LZO2A_999 (and many more).
-  const ENCODING_XZ       = 7;
+  const ENCODING_XZ       = 7; // currently unsupported due to available libraries being defunct.
   const ENCODING_EXI      = 8;
   const ENCODING_IDENTITY = 9;
   const ENCODING_SDCH     = 10;
@@ -308,6 +303,32 @@ class c_base_http extends c_base_rfc_string {
   }
 
   /**
+   * Returns a list of HTTP headers that can be used as an HTML meta tag.
+   *
+   * The relationship between HTTP headers and HTML headers is not always one to one.
+   * These should be used
+   * @todo: this list will need to be reviewed once I work on the HTML meta handling code.
+   *
+   * The HTML language supports HTTP headers as HTML tags.
+   *
+   * @see: https://html.spec.whatwg.org/multipage/semantics.html#standard-metadata-names
+   * @see: https://www.w3.org/TR/html5/document-metadata.html#the-meta-element
+   */
+  public function get_response_headers_for_meta() {
+    return array(
+      self::RESPONSE_CACHE_CONTROL => self::RESPONSE_CACHE_CONTROL,
+      self::RESPONSE_CONTENT_ENCODING => self::RESPONSE_CONTENT_ENCODING,
+      self::RESPONSE_CONTENT_LANGUAGE => self::RESPONSE_CONTENT_LANGUAGE,
+      self::RESPONSE_CONTENT_SECURITY_POLICY => self::RESPONSE_CONTENT_SECURITY_POLICY,
+      self::RESPONSE_CONTENT_TYPE => self::RESPONSE_CONTENT_TYPE,
+      self::RESPONSE_EXPIRES => self::RESPONSE_EXPIRES,
+      self::RESPONSE_LINK => self::RESPONSE_LINK,
+      self::RESPONSE_PRAGMA => self::RESPONSE_PRAGMA,
+      self::RESPONSE_REFRESH => self::RESPONSE_REFRESH,
+    );
+  }
+
+  /**
    * Get the HTTP request array.
    *
    * Load the entire HTTP request array or a specific request field.
@@ -570,21 +591,18 @@ class c_base_http extends c_base_rfc_string {
       unset($headers['accept_datetime']);
     }
 
-    // @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
     if (array_key_exists('access_control_request_method', $this->headers)) {
-      $this->p_load_request_rawish('access_control_request_method', $this->REQUEST_ACCESS_CONTROL_REQUEST_METHOD, 256);
+      $this->p_load_request_access_control_request_method();
       unset($headers['access_control_request_method']);
     }
 
-    // @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
     if (array_key_exists('access_control_request_headers', $this->headers)) {
-      $this->p_load_request_rawish('access_request_allow_headers', $this->REQUEST_ACCESS_CONTROL_REQUEST_HEADERS, 256);
+      $this->p_load_request_access_control_request_headers();
       unset($headers['access_control_request_headers']);
     }
 
-    // @see: https://tools.ietf.org/html/rfc7235#section-4.2
     if (array_key_exists('authorization', $this->headers)) {
-      $this->p_load_request_rawish('authorization', $this->REQUEST_AUTHORIZATION, 4096);
+      $this->p_load_request_authorization();
       unset($headers['authorization']);
     }
 
@@ -688,9 +706,8 @@ class c_base_http extends c_base_rfc_string {
       unset($headers['origin']);
     }
 
-    // @see: https://tools.ietf.org/html/rfc7235#section-4.4
     if (array_key_exists('proxy_authorization', $this->headers)) {
-      $this->p_load_request_rawish('proxy_authorization', $this->REQUEST_PROXY_AUTHORIZATION, 4096);
+      $this->p_load_request_proxy_authorization();
       unset($headers['proxy_authorization']);
     }
 
@@ -725,22 +742,22 @@ class c_base_http extends c_base_rfc_string {
     }
 
     if (array_key_exists('x_requested_with', $this->headers)) {
-      $this->p_load_request_rawish('x_requested_with', $this->REQUEST_X_REQUESTED_WITH, 64);
+      $this->p_load_request_x_requested_with();
       unset($headers['x_requested_with']);
     }
 
     if (array_key_exists('x_forwarded_for', $this->headers)) {
-      $this->p_load_request_rawish('x_forwarded_for', $this->REQUEST_X_FORWARDED_for, 512);
+      $this->p_load_request_x_requested_for();
       unset($headers['x_forwarded_for']);
     }
 
     if (array_key_exists('x_forwarded_host', $this->headers)) {
-      $this->p_load_request_rawish('x_forwarded_host', $this->REQUEST_X_FORWARDED_HOST, 512);
+      $this->p_load_request_x_requested_host();
       unset($headers['x_forwarded_host']);
     }
 
     if (array_key_exists('x_forwarded_proto', $this->headers)) {
-      $this->p_load_request_rawish('x_forwarded_proto', $this->REQUEST_X_FORWARDED_PROTO, 64);
+      $this->p_load_request_x_requested_proto();
       unset($headers['x_forwarded_proto']);
     }
 
@@ -776,8 +793,9 @@ class c_base_http extends c_base_rfc_string {
   /**
    * Assign HTTP response header: access-control-allow-origin.
    *
-   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
-   *        More work is likely needed to be done with this and its structure is subject to change.
+   * Note on multiple urls: The standard appears to only support one url.
+   * Therefore, to have multiple urls, the clients ORIGIN should be checked against a known list.
+   * Then, from that list either the default or the clients ORIGIN is sent.
    *
    * @param string $uri
    *   The uri to assign to the specified header.
@@ -788,6 +806,8 @@ class c_base_http extends c_base_rfc_string {
    *   FALSE with error bit set is returned on error.
    *
    * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Origin
    */
   public function set_response_access_control_allow_origin($uri) {
     if (!is_string($uri)) {
@@ -800,13 +820,24 @@ class c_base_http extends c_base_rfc_string {
       $this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN] = array('wildcard' => TRUE);
     }
     else {
-      $parsed = $this->p_parse_uri($uri);
+      $text = $this->pr_rfc_string_prepare($uri);
+      if ($text['invalid']) {
+        unset($text);
+
+        $error = c_base_error::s_log(NULL, array('arguments' => array(':operation_name' => 'this->pr_rfc_string_prepare', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::OPERATION_FAILURE);
+        return c_base_return_error::s_false($error);
+      }
+
+      $parsed = $this->pr_rfc_string_is_uri($text['ordinals'], $text['characters']);
+      unset($text);
+
       if ($parsed['invalid']) {
         unset($parsed);
         $error = c_base_error::s_log(NULL, array('arguments' => array(':format_name' => 'uri', ':expected_format' => NULL, ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::INVALID_FORMAT);
         return c_base_return_error::s_false($error);
       }
       unset($parsed['invalid']);
+      unset($parsed['current']);
 
       $parsed['wildcard'] = FALSE;
       $this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN] = $parsed;
@@ -819,9 +850,6 @@ class c_base_http extends c_base_rfc_string {
   /**
    * Assign HTTP response header: access-control-allow-credentials.
    *
-   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
-   *        More work is likely needed to be done with this and its structure is subject to change.
-   *
    * @param bool $allow_credentials
    *   The value to assign to the specified header.
    *
@@ -830,6 +858,8 @@ class c_base_http extends c_base_rfc_string {
    *   FALSE with error bit set is returned on error.
    *
    * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Credentials
    */
   public function set_response_access_control_allow_credentials($allow_credentials) {
     if (!is_bool($allow_credentials)) {
@@ -844,10 +874,6 @@ class c_base_http extends c_base_rfc_string {
   /**
    * Assign HTTP response header: access-control-expose-headers.
    *
-   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
-   *        More work is likely needed to be done with this and its structure is subject to change.
-   * @todo: should this be written in the same way as set_response_allow()?
-   *
    * @param string $header_name
    *   The header name to assign to the specified header.
    * @param bool $append
@@ -859,6 +885,8 @@ class c_base_http extends c_base_rfc_string {
    *   FALSE with error bit set is returned on error.
    *
    * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Expose-Headers
    */
   public function set_response_access_control_expose_headers($header_name, $append = TRUE) {
     if (!is_string($header_name)) {
@@ -898,34 +926,30 @@ class c_base_http extends c_base_rfc_string {
   /**
    * Assign HTTP response header: access-control-max-age.
    *
-   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
-   *        More work is likely needed to be done with this and its structure is subject to change.
-   *
-   * @param int|float $timestamp
-   *   The timestamp to assign to the specified header.
+   * @param int|float $seconds
+   *   The seconds to assign to the specified header.
    *
    * @return c_base_return_status
    *   TRUE on success, FALSE otherwise.
    *   FALSE with error bit set is returned on error.
    *
    * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Max-Age
    */
-  public function set_response_access_control_max_age($timestamp) {
-    if (!is_int($timestamp) && !is_float($timestamp)) {
-      $error = c_base_error::s_log(NULL, array('arguments' => array(':argument_name' => 'timestamp', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::INVALID_ARGUMENT);
+  public function set_response_access_control_max_age($seconds) {
+    if (!is_int($seconds) && !is_float($seconds)) {
+      $error = c_base_error::s_log(NULL, array('arguments' => array(':argument_name' => 'seconds', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::INVALID_ARGUMENT);
       return c_base_return_error::s_false($error);
     }
 
-    $this->response[self::RESPONSE_ACCESS_CONTROL_MAX_AGE] = $timestamp;
+    $this->response[self::RESPONSE_ACCESS_CONTROL_MAX_AGE] = $seconds;
     return new c_base_return_true();
   }
 
   /**
    * Assign HTTP response header: access-control-allow-methods.
    *
-   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
-   *        More work is likely needed to be done with this and its structure is subject to change.
-   *
    * @param int $method
    *   The code representing the method to assign to the specified header.
    * @param bool $append
@@ -937,6 +961,8 @@ class c_base_http extends c_base_rfc_string {
    *   FALSE with error bit set is returned on error.
    *
    * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Methods
    */
   public function set_response_access_control_allow_methods($method, $append = TRUE) {
     if (!is_int($method)) {
@@ -950,6 +976,34 @@ class c_base_http extends c_base_rfc_string {
     }
 
 
+    // this method does nothing.
+    if ($method === self::HTTP_METHOD_NONE) {
+      return new c_base_return_true();
+    }
+
+
+    // require only valid/known methods.
+    switch ($method) {
+      case self::HTTP_METHOD_NONE:
+      case self::HTTP_METHOD_GET:
+      case self::HTTP_METHOD_HEAD:
+      case self::HTTP_METHOD_POST:
+      case self::HTTP_METHOD_PUT:
+      case self::HTTP_METHOD_DELETE:
+      case self::HTTP_METHOD_TRACE:
+      case self::HTTP_METHOD_OPTIONS:
+      case self::HTTP_METHOD_CONNECT:
+      case self::HTTP_METHOD_PATCH:
+      case self::HTTP_METHOD_TRACK:
+      case self::HTTP_METHOD_DEBUG:
+        break;
+
+      default:
+        $error = c_base_error::s_log(NULL, array('arguments' => array(':argument_name' => 'method', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::INVALID_ARGUMENT);
+        return c_base_return_error::s_false($error);
+      }
+
+
     if ($append) {
       if (!isset($this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS]) || !is_array($this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS])) {
         $this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS] = array();
@@ -967,9 +1021,6 @@ class c_base_http extends c_base_rfc_string {
   /**
    * Assign HTTP response header: access-control-allow-headers.
    *
-   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
-   *        More work is likely needed to be done with this and its structure is subject to change.
-   *
    * @param string $header_name
    *   The header name to assign to the specified header.
    * @param bool $append
@@ -977,6 +1028,8 @@ class c_base_http extends c_base_rfc_string {
    *   If FALSE, then assign the header name.
    *
    * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Headers
    */
   public function set_response_access_control_allow_headers($header_name, $append = TRUE) {
     if (!is_string($header_name)) {
@@ -2148,10 +2201,10 @@ class c_base_http extends c_base_rfc_string {
    * The standard defines this as:
    * - (uri) *(";" 1*(tchar) "=" 1*(1*(tchar) / 1*(quoted-string)))
    *
-   * @param string|null $uri
+   * This standard likely supports multiple link headers.
+   *
+   * @param string $uri
    *   The URI to assign to the specified header.
-   *   May be NULL when append is TRUE.
-   *   Both $null and $parameter_name may not be NULL.
    * @param string|null $parameter_name
    *   (optional) A single link parameter to be added.
    *   If NULL, then this is ignored.
@@ -2160,8 +2213,9 @@ class c_base_http extends c_base_rfc_string {
    *   (optional) A single link value to be added.
    *   If NULL, then this is ignored.
    * @param bool $append
-   *   (optional) If TRUE, then append the header name.
-   *   If FALSE, then assign the header name.
+   *   (optional) If TRUE, then append the parameter name.
+   *   If FALSE, then assign the parameter name.
+   *   When $parameter_name is NULL and this is FALSE, then assign an empty array for parameters associated with $uri.
    *
    * @return c_base_return_status
    *   TRUE on success, FALSE otherwise.
@@ -2202,7 +2256,17 @@ class c_base_http extends c_base_rfc_string {
     }
 
 
-    $parsed_uri = $this->p_parse_uri($uri);
+    $text = $this->pr_rfc_string_prepare($uri);
+    if ($text['invalid']) {
+      unset($text);
+
+      $error = c_base_error::s_log(NULL, array('arguments' => array(':operation_name' => 'this->pr_rfc_string_prepare', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::OPERATION_FAILURE);
+      return c_base_return_error::s_false($error);
+    }
+
+    $parsed = $this->pr_rfc_string_is_uri($text['ordinals'], $text['characters']);
+    unset($text);
+
     if ($parsed_uri['invalid']) {
       unset($parsed_uri);
 
@@ -2212,6 +2276,22 @@ class c_base_http extends c_base_rfc_string {
     unset($parsed_uri['invalid']);
 
 
+    // when append is FALSE and there is no parameter name, then assign url instead of appending url.
+    if (!$append && is_null($parameter_name)) {
+      if (!isset($this->response[self::RESPONSE_LINK])) {
+        $this->response[self::RESPONSE_LINK] = array();
+      }
+
+      $this->response[self::RESPONSE_LINK][$uri] = array(
+        'uri' => $parsed_uri,
+        'parameters' => array(),
+      );
+      unset($parsed_uri);
+
+      return new c_base_return_true();
+    }
+
+
     $prepared_parameter_name = NULL;
     if (is_string($parameter_name)) {
       $prepared_parameter_name = $this->p_prepare_token($parameter_name);
@@ -2260,31 +2340,23 @@ class c_base_http extends c_base_rfc_string {
 
 
     if (!isset($this->response[self::RESPONSE_LINK])) {
-      // uri cannot be NULL if there is no uri currently assigned.
-      if (is_null($uri)) {
-        unset($prepared_token);
-
-        $error = c_base_error::s_log(NULL, array('arguments' => array(':argument_name' => 'uri', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::INVALID_ARGUMENT);
-        return c_base_return_error::s_false($error);
-      }
-
-      $this->response[self::RESPONSE_LINK] = array(
-        'uri' => NULL,
-        'parameters' => array(),
-      );
+      $this->response[self::RESPONSE_LINK] = array();
     }
 
-    if (is_string($uri)) {
-      $this->response[self::RESPONSE_LINK]['uri'] = $parsed_uri;
+    if (!array_key_exists($uri, $this->response[self::RESPONSE_LINK])) {
+      $this->response[self::RESPONSE_LINK][$uri] = array(
+        'uri' => $parsed_uri,
+        'parameters' => array()
+      );
     }
     unset($parsed_uri);
 
     if (is_string($parameter_name)) {
       if ($append) {
-        $this->response[self::RESPONSE_LINK]['parameters'][$prepared_parameter_name] = $parsed_parameter_value['text'];
+        $this->response[self::RESPONSE_LINK][$uri]['parameters'][$prepared_parameter_name] = $parsed_parameter_value['text'];
       }
       else {
-        $this->response[self::RESPONSE_LINK]['parameters'] = array($prepared_parameter_name => $parsed_parameter_value['text']);
+        $this->response[self::RESPONSE_LINK][$uri]['parameters'] = array($prepared_parameter_name => $parsed_parameter_value['text']);
       }
     }
     unset($prepared_parameter_name);
@@ -2313,7 +2385,18 @@ class c_base_http extends c_base_rfc_string {
       return c_base_return_error::s_false($error);
     }
 
-    $parsed = $this->p_parse_uri($uri);
+
+    $text = $this->pr_rfc_string_prepare($uri);
+    if ($text['invalid']) {
+      unset($text);
+
+      $error = c_base_error::s_log(NULL, array('arguments' => array(':operation_name' => 'this->pr_rfc_string_prepare', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::OPERATION_FAILURE);
+      return c_base_return_error::s_false($error);
+    }
+
+    $parsed = $this->pr_rfc_string_is_uri($text['ordinals'], $text['characters']);
+    unset($text);
+
     if ($parsed['invalid']) {
       unset($parsed);
       $error = c_base_error::s_log(NULL, array('arguments' => array(':format_name' => 'uri', ':expected_format' => NULL, ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::INVALID_FORMAT);
@@ -2675,9 +2758,7 @@ class c_base_http extends c_base_rfc_string {
       unset($this->response[self::RESPONSE_CONTENT_LENGTH]);
     }
 
-    // RESPONSE_TRANSFER_ENCODING
-
-    // @todo
+    // @todo: self::RESPONSE_TRANSFER_ENCODING
 
     $error = c_base_error::s_log(NULL, array('arguments' => array(':functionality_name' => 'http response transfer encoding', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::NO_SUPPORT);
     return c_base_return_error::s_false($error);
@@ -2727,7 +2808,7 @@ class c_base_http extends c_base_rfc_string {
     }
 
 
-    $prepared_token = $this->p_prepare_token($header_name);
+    $prepared_token = $this->p_prepare_token($header_name, FALSE);
     if ($prepared_token === FALSE) {
       unset($prepared_token);
       $error = c_base_error::s_log(NULL, array('arguments' => array(':operation_name' => 'this->p_prepare_token', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::OPERATION_FAILURE);
@@ -3357,15 +3438,14 @@ class c_base_http extends c_base_rfc_string {
   /**
    * Obtain HTTP response header: access-control-allow-origin.
    *
-   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
-   *        More work is likely needed to be done with this and its structure is subject to change.
-   *
    * @return c_base_return_array|c_base_return_status
    *   A decoded uri split into its different parts inside an array.
    *   This array also contains a key called 'wildcard' which may be either TRUE or FALSE.
    *   FALSE with error bit set is returned on error, including when the key is not defined.
    *
    * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Origin
    */
   public function get_response_access_control_allow_origin() {
     if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN, $this->response)) {
@@ -3379,14 +3459,14 @@ class c_base_http extends c_base_rfc_string {
   /**
    * Obtain HTTP response header: access-control-allow-credentials.
    *
-   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
-   *        More work is likely needed to be done with this and its structure is subject to change.
-   *
    * @return c_base_return_bool|c_base_return_status
    *   A boolean representing whether or not to allow credentials.
    *   FALSE with error bit set is returned on error, including when the key is not defined.
    *
    * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Credentials
+   *
    */
   public function get_response_access_control_allow_credentials() {
     if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_ALLOW_CREDENTIALS, $this->response)) {
@@ -3400,14 +3480,13 @@ class c_base_http extends c_base_rfc_string {
   /**
    * Obtain HTTP response header: access-control-expose-headers.
    *
-   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
-   *        More work is likely needed to be done with this and its structure is subject to change.
-   *
    * @return c_base_return_array|c_base_return_status
    *   An array of headers to expose.
    *   FALSE with error bit set is returned on error, including when the key is not defined.
    *
    * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Expose-Headers
    */
   public function get_response_access_control_expose_headers() {
     if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_EXPOSE_HEADERS, $this->response)) {
@@ -3421,14 +3500,13 @@ class c_base_http extends c_base_rfc_string {
   /**
    * Obtain HTTP response header: access-control-max-age.
    *
-   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
-   *        More work is likely needed to be done with this and its structure is subject to change.
-   *
    * @return c_base_return_int|c_base_return_status
    *   An Unix timestamp representing the specified header.
    *   FALSE with error bit set is returned on error, including when the key is not defined.
    *
    * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Max-Age
    */
   public function get_response_access_control_max_age() {
     if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_MAX_AGE, $this->response)) {
@@ -3442,13 +3520,12 @@ class c_base_http extends c_base_rfc_string {
   /**
    * Obtain HTTP response header: access-control-allow-methods.
    *
-   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
-   *        More work is likely needed to be done with this and its structure is subject to change.
-   *
    * @return c_base_return_array|c_base_return_status
    *   FALSE with error bit set is returned on error, including when the key is not defined.
    *
    * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Methods
    */
   public function get_response_access_control_allow_methods() {
     if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS, $this->response)) {
@@ -3462,12 +3539,13 @@ class c_base_http extends c_base_rfc_string {
   /**
    * Obtain HTTP response header: access-control-allow-headers.
    *
-   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
-   *        More work is likely needed to be done with this and its structure is subject to change.
-   *
    * @return c_base_return_arrayl|c_base_return_status
    *   An array of allowed headers is returned.
    *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Headers
    */
   public function get_response_access_control_allow_headers() {
     if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS, $this->response)) {
@@ -4447,7 +4525,7 @@ class c_base_http extends c_base_rfc_string {
     }
 
     $this->p_prepare_header_response_access_control_allow_origin($header_id_to_names[self::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN], $header_output);
-    $this->p_prepare_header_response_simple_value($header_id_to_names[self::RESPONSE_ACCESS_CONTROL_ALLOW_CREDENTIALS], $header_output, self::RESPONSE_ACCESS_CONTROL_ALLOW_CREDENTIALS);
+    $this->p_prepare_header_response_boolean_value($header_id_to_names[self::RESPONSE_ACCESS_CONTROL_ALLOW_CREDENTIALS], $header_output, self::RESPONSE_ACCESS_CONTROL_ALLOW_CREDENTIALS);
     $this->p_prepare_header_response_access_control_expose_headers($header_id_to_names[self::RESPONSE_ACCESS_CONTROL_EXPOSE_HEADERS], $header_output);
     $this->p_prepare_header_response_simple_value($header_id_to_names[self::RESPONSE_ACCESS_CONTROL_MAX_AGE], $header_output, self::RESPONSE_ACCESS_CONTROL_MAX_AGE);
     $this->p_prepare_header_response_access_control_allow_methods($header_id_to_names[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS], $header_output);
@@ -4804,7 +4882,7 @@ class c_base_http extends c_base_rfc_string {
       krsort($this->request[self::REQUEST_ACCEPT]['data']['weight']);
 
       // The NULL key should be the first key in the weight.
-      $this->p_prepend_array_value(NULL, $this->request[self::REQUEST_ACCEPT]['data']['weight']);
+      $this->pr_prepend_array_value(NULL, $this->request[self::REQUEST_ACCEPT]['data']['weight']);
 
       // rename 'choices' array key to 'accept'.
       $this->request[self::REQUEST_ACCEPT]['data']['accept'] = $this->request[self::REQUEST_ACCEPT]['data']['choices'];
@@ -4875,7 +4953,7 @@ class c_base_http extends c_base_rfc_string {
       krsort($this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['weight']);
 
       // The NULL key should be the first key in the weight.
-      $this->p_prepend_array_value(NULL, $this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['weight']);
+      $this->pr_prepend_array_value(NULL, $this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['weight']);
 
       // rename 'choices' array key to 'language'.
       $this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['language'] = $this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['choices'];
@@ -4986,7 +5064,7 @@ class c_base_http extends c_base_rfc_string {
       krsort($this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight']);
 
       // The NULL key should be the first key in the weight.
-      $this->p_prepend_array_value(NULL, $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight']);
+      $this->pr_prepend_array_value(NULL, $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight']);
 
       // rename 'choices' array key to 'encoding'.
       $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['encoding'] = $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['choices'];
@@ -5142,7 +5220,7 @@ class c_base_http extends c_base_rfc_string {
       krsort($this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight']);
 
       // The NULL key should be the first key in the weight.
-      $this->p_prepend_array_value(NULL, $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight']);
+      $this->pr_prepend_array_value(NULL, $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight']);
     }
     unset($this->request[self::REQUEST_ACCEPT_CHARSET]['data']['invalid']);
 
@@ -5177,6 +5255,167 @@ class c_base_http extends c_base_rfc_string {
   }
 
   /**
+   * Load and process the HTTP request parameter: accept-datetime.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Request-Method
+   */
+  private function p_load_request_access_control_request_method() {
+    $method_string = mb_strtolower($this->headers['access_control_request_method']);
+    $method_string = str_replace(' ', '', $method_string);
+
+    $methods = explode(',', $method_string);
+    unset($method_string);
+
+    if (empty($methods)) {
+      $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['invalid'] = TRUE;
+      return;
+    }
+
+    $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['data'] = array();
+
+    foreach ($methods as $method) {
+      switch ($method) {
+        case 'get':
+          $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['data'][self::HTTP_METHOD_GET] = self::HTTP_METHOD_GET;
+          break;
+
+        case 'head':
+          $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['data'][self::HTTP_METHOD_HEAD] = self::HTTP_METHOD_HEAD;
+          break;
+
+        case 'post':
+          $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['data'][self::HTTP_METHOD_POST] = self::HTTP_METHOD_POST;
+          break;
+
+        case 'put':
+          $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['data'][self::HTTP_METHOD_PUT] = self::HTTP_METHOD_PUT;
+          break;
+
+        case 'delete':
+          $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['data'][self::HTTP_METHOD_DELETE] = self::HTTP_METHOD_DELETE;
+          break;
+
+        case 'trace':
+          $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['data'][self::HTTP_METHOD_TRACE] = self::HTTP_METHOD_TRACE;
+          break;
+
+        case 'options':
+          $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['data'][self::HTTP_METHOD_OPTIONS] = self::HTTP_METHOD_OPTIONS;
+          break;
+
+        case 'connect':
+          $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['data'][self::HTTP_METHOD_CONNECT] = self::HTTP_METHOD_CONNECT;
+          break;
+
+        case 'patch':
+          $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['data'][self::HTTP_METHOD_PATCH] = self::HTTP_METHOD_PATCH;
+          break;
+
+        case 'track':
+          $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['data'][self::HTTP_METHOD_TRACK] = self::HTTP_METHOD_TRACK;
+          break;
+
+        case 'debug':
+          $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['data'][self::HTTP_METHOD_DEBUG] = self::HTTP_METHOD_DEBUG;
+          break;
+
+        default:
+          // skip unknown methods instead of failing so that any discovered known methods are still loaded.
+          break;
+      }
+    }
+    unset($method);
+
+    if (!empty($methods) && empty($this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['data'])) {
+      unset($methods);
+
+      // no valid methods were found, now the error can be reported.
+      $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['invalid'] = TRUE;
+      return;
+    }
+    unset($methods);
+
+    $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['defined'] = TRUE;
+    $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: accept-datetime.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Request-Headers
+   */
+  private function p_load_request_access_control_request_headers() {
+    if (empty($this->headers['access_control_request_headers'])) {
+      $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_HEADERS]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['access_control_request_headers']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_HEADERS]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_HEADERS]['data'] = $this->pr_rfc_string_is_token($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_HEADERS]['data']['invalid']) {
+      $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_HEADERS]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_HEADERS]['defined'] = TRUE;
+
+      // rename 'tokens' array key to 'headers'.
+      $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_HEADERS]['data']['headers'] = $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_HEADERS]['data']['tokens'];
+      unset($this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_HEADERS]['data']['tokens']);
+    }
+    unset($this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_HEADERS]['data']['invalid']);
+    unset($this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_HEADERS]['data']['current']);
+
+    $this->request[self::REQUEST_ACCESS_CONTROL_REQUEST_HEADERS]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: authorization.
+   *
+   * 1*(tchar) 1*(wsp) 1*(tchar) *(wsp) "=" *(wsp) ( 1*(tchar) / quoted-string ) *( *(wsp) "," *(wsp) 1*(tchar) *(wsp) "=" *(wsp) ( 1*(tchar) / quoted-string ) ).
+   *
+   * @see: https://tools.ietf.org/html/rfc7235#section-4.2
+   */
+  private function p_load_request_authorization() {
+    if (empty($this->headers['authorization'])) {
+      $this->request[self::REQUEST_AUTHORIZATION]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['authorization']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_AUTHORIZATION]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    $this->request[self::REQUEST_AUTHORIZATION]['data'] = $this->pr_rfc_string_is_credentials($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_AUTHORIZATION]['data']['invalid']) {
+      $this->request[self::REQUEST_AUTHORIZATION]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_AUTHORIZATION]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_AUTHORIZATION]['data']['invalid']);
+    unset($this->request[self::REQUEST_AUTHORIZATION]['data']['current']);
+
+    $this->request[self::REQUEST_AUTHORIZATION]['invalid'] = FALSE;
+  }
+
+  /**
    * Load and process the HTTP request parameter: cache-control.
    *
    * Only 'no-cache' is supported at this time for requests.
@@ -5283,7 +5522,7 @@ class c_base_http extends c_base_rfc_string {
 
       // rename 'tokens' array key to 'connection'.
       $this->request[self::REQUEST_CONNECTION]['data']['connection'] = $this->request[self::REQUEST_CONNECTION]['data']['tokens'];
-      unset($this->request[self::REQUEST_CONNECTION]['data']['tokens']);
+      unset($this->request[self::REQUEST_CONNECTION]['data']['text']);
     }
     unset($this->request[self::REQUEST_CONNECTION]['data']['invalid']);
     unset($this->request[self::REQUEST_CONNECTION]['data']['current']);
@@ -5505,7 +5744,16 @@ class c_base_http extends c_base_rfc_string {
       return;
     }
 
-    $this->request[self::REQUEST_HOST]['data'] = $this->p_parse_uri($this->headers['host']);
+    $text = $this->pr_rfc_string_prepare($this->headers['host']);
+    if ($text['invalid']) {
+      unset($text);
+
+      $this->request[self::REQUEST_HOST]['invalid'] = TRUE;
+      return;
+    }
+
+    $this->request[self::REQUEST_HOST]['data'] = $this->pr_rfc_string_is_uri($text['ordinals'], $text['characters']);
+    unset($text);
 
     if ($this->request[self::REQUEST_HOST]['data']['invalid']) {
       $this->request[self::REQUEST_HOST]['invalid'] = TRUE;
@@ -5754,6 +6002,7 @@ class c_base_http extends c_base_rfc_string {
    *
    * @see: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
    * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
    */
   private function p_load_request_origin() {
     if (empty($this->headers['origin'])) {
@@ -5761,7 +6010,16 @@ class c_base_http extends c_base_rfc_string {
       return;
     }
 
-    $this->request[self::REQUEST_ORIGIN]['data'] = $this->p_parse_uri($this->headers['origin']);
+    $text = $this->pr_rfc_string_prepare($this->headers['origin']);
+    if ($text['invalid']) {
+      unset($text);
+
+      $this->request[self::REQUEST_ORIGIN]['invalid'] = TRUE;
+      return;
+    }
+
+    $this->request[self::REQUEST_ORIGIN]['data'] = $this->pr_rfc_string_is_uri($text['ordinals'], $text['characters']);
+    unset($text);
 
     if ($this->request[self::REQUEST_ORIGIN]['data']['invalid']) {
       $this->request[self::REQUEST_ORIGIN]['invalid'] = TRUE;
@@ -5775,6 +6033,41 @@ class c_base_http extends c_base_rfc_string {
   }
 
   /**
+   * Load and process the HTTP request parameter: proxy-authorization.
+   *
+   * 1*(tchar) 1*(wsp) 1*(tchar) *(wsp) "=" *(wsp) ( 1*(tchar) / quoted-string ) *( *(wsp) "," *(wsp) 1*(tchar) *(wsp) "=" *(wsp) ( 1*(tchar) / quoted-string ) ).
+   *
+   * @see: https://tools.ietf.org/html/rfc7235#section-4.4
+   */
+  private function p_load_request_proxy_authorization() {
+    if (empty($this->headers['proxy_authorization'])) {
+      $this->request[self::REQUEST_PROXY_AUTHORIZATION]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['proxy_authorization']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_PROXY_AUTHORIZATION]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    $this->request[self::REQUEST_PROXY_AUTHORIZATION]['data'] = $this->pr_rfc_string_is_credentials($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_PROXY_AUTHORIZATION]['data']['invalid']) {
+      $this->request[self::REQUEST_PROXY_AUTHORIZATION]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_PROXY_AUTHORIZATION]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_PROXY_AUTHORIZATION]['data']['invalid']);
+    unset($this->request[self::REQUEST_PROXY_AUTHORIZATION]['data']['current']);
+
+    $this->request[self::REQUEST_PROXY_AUTHORIZATION]['invalid'] = FALSE;
+  }
+
+  /**
    * Load and process the HTTP request parameter: referer.
    *
    * @see: https://tools.ietf.org/html/rfc7231#section-5.5.2
@@ -5785,7 +6078,16 @@ class c_base_http extends c_base_rfc_string {
       return;
     }
 
-    $this->request[self::REQUEST_REFERER]['data'] = $this->p_parse_uri($this->headers['referer']);
+    $text = $this->pr_rfc_string_prepare($this->headers['referer']);
+    if ($text['invalid']) {
+      unset($text);
+
+      $this->request[self::REQUEST_REFERER]['invalid'] = TRUE;
+      return;
+    }
+
+    $this->request[self::REQUEST_REFERER]['data'] = $this->pr_rfc_string_is_uri($text['ordinals'], $text['characters']);
+    unset($text);
 
     if ($this->request[self::REQUEST_REFERER]['data']['invalid']) {
       $this->request[self::REQUEST_REFERER]['invalid'] = TRUE;
@@ -6013,13 +6315,152 @@ class c_base_http extends c_base_rfc_string {
   }
 
   /**
-   * Load and process the HTTP request parameter: checksum_header.
-   *
-   * The following format is expected:
-   * - 1*(atext):1*(atext):1*(atext)
+   * Load and process the HTTP request parameter: x-requested-with.
    *
-   * The header should have either 'partial:checksum_type:checksum_value' or 'complete:checksum_type:checksum_value'.
-   * The checksum_value is stored in base64.
+   * I am just assuming the syntax to be 1*(tchar).
+   *
+   * @see: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
+   */
+  private function p_load_request_x_requested_with() {
+    if (empty($this->headers['x-requested-with'])) {
+      $this->request[self::REQUEST_X_REQUESTED_WITH]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['x-requested-with']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_X_REQUESTED_WITH]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    $this->request[self::REQUEST_X_REQUESTED_WITH]['data'] = $this->pr_rfc_string_is_token($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_X_REQUESTED_WITH]['data']['invalid']) {
+      $this->request[self::REQUEST_X_REQUESTED_WITH]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_X_REQUESTED_WITH]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_X_REQUESTED_WITH]['data']['invalid']);
+
+    $this->request[self::REQUEST_X_REQUESTED_WITH]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: x-requested-for.
+   *
+   * This could be a name or an ip address.
+   * I am just using 1*(tchar) because that also allows ip addresses.
+   *
+   * @see: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
+   */
+  private function p_load_request_x_requested_for() {
+    if (empty($this->headers['x-requested-for'])) {
+      $this->request[self::REQUEST_X_REQUESTED_FOR]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['x-requested-for']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_X_REQUESTED_FOR]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    $this->request[self::REQUEST_X_REQUESTED_HOST]['data'] = $this->pr_rfc_string_is_token($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_X_REQUESTED_HOST]['data']['invalid']) {
+      $this->request[self::REQUEST_X_REQUESTED_HOST]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_X_REQUESTED_HOST]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_X_REQUESTED_HOST]['data']['invalid']);
+    unset($this->request[self::REQUEST_X_REQUESTED_HOST]['data']['uri']);
+
+    $this->request[self::REQUEST_X_REQUESTED_HOST]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: x-requested-host.
+   *
+   * I am just assuming the syntax to be (url).
+   *
+   * @see: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
+   */
+  private function p_load_request_x_requested_host() {
+    if (empty($this->headers['x-requested-host'])) {
+      $this->request[self::REQUEST_X_REQUESTED_HOST]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['x-requested-host']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_X_REQUESTED_HOST]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    $this->request[self::REQUEST_X_REQUESTED_HOST]['data'] = $this->pr_rfc_string_is_uri($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_X_REQUESTED_HOST]['data']['invalid'] || $this->request[self::REQUEST_X_REQUESTED_HOST]['data']['uri'] === FALSE) {
+      $this->request[self::REQUEST_X_REQUESTED_HOST]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_X_REQUESTED_HOST]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_X_REQUESTED_HOST]['data']['invalid']);
+    unset($this->request[self::REQUEST_X_REQUESTED_HOST]['data']['uri']);
+
+    $this->request[self::REQUEST_X_REQUESTED_HOST]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: x-requested-proto.
+   *
+   * I am just assuming the syntax to be 1*(tchar).
+   *
+   * @see: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
+   */
+  private function p_load_request_x_requested_proto() {
+    if (empty($this->headers['x-requested-proto'])) {
+      $this->request[self::REQUEST_X_REQUESTED_PROTO]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['x-requested-proto']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_X_REQUESTED_PROTO]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    $this->request[self::REQUEST_X_REQUESTED_PROTO]['data'] = $this->pr_rfc_string_is_token($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_X_REQUESTED_PROTO]['data']['invalid']) {
+      $this->request[self::REQUEST_X_REQUESTED_PROTO]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_X_REQUESTED_PROTO]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_X_REQUESTED_PROTO]['data']['invalid']);
+
+    $this->request[self::REQUEST_X_REQUESTED_PROTO]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: checksum_header.
+   *
+   * The following format is expected:
+   * - 1*(atext):1*(atext):1*(atext)
+   *
+   * The header should have either 'partial:checksum_type:checksum_value' or 'complete:checksum_type:checksum_value'.
+   * The checksum_value is stored in base64.
    *
    * The content checksum represents the checksum of the HTTP content (HTTP packet body).
    * This should already be defined if and when a checksum_header is used.
@@ -6290,253 +6731,6 @@ class c_base_http extends c_base_rfc_string {
   }
 
   /**
-   * Decode and check that the given uri is valid.
-   *
-   * This does not decode the uri, it separates it into its individual parts.
-   *
-   * Validation is done according to rfc3986.
-   *
-   *   foo://example.com:8042/over/there?name=ferret#nose
-   *   \_/   \______________/\_________/ \_________/ \__/
-   *    |           |            |            |        |
-   *  scheme    authority       path        query   fragment
-   *    |   _____________________|__
-   *   / \ /                        \
-   *   urn:example:animal:ferret:nose
-   *
-   *
-   * @param string $uri
-   *   The url to validate and decode.
-   *
-   * @return array
-   *   A decoded uri split into its different parts inside an array.
-   *   An array key called 'invalid' exists to designate that the uri is invalid.
-   *
-   * @see: https://tools.ietf.org/html/rfc3986
-   */
-  private function p_parse_uri($uri) {
-    $result = array(
-      'scheme' => NULL,
-      'authority' => NULL,
-      'path' => NULL,
-      'query' => NULL,
-      'fragment' => NULL,
-      'url' => TRUE, // @todo: set to FALSE when uri is a urn instead of a url.
-      'invalid' => FALSE,
-    );
-
-    // @todo: completely rewrite below.
-    $result['invalid'] = TRUE;
-/*
-    $matches = array();
-    $matched = preg_match('!^((\w[=|\w|\d|\s|\.|-|_|~|%]*)*:)*([^#|?]*)(\?([^#]+))*(#(.+))*$!iu', $uri, $matches);
-
-    if ($matched == FALSE || !array_key_exists(3, $matches)) {
-      unset($address);
-      unset($matches);
-      unset($matched);
-      $result['invalid'] = TRUE;
-      return $result;
-    }
-    unset($matched);
-
-
-    // process scheme.
-    if (array_key_exists(2, $matches) && self::p_length_string($matches[2]) > 0) {
-      $combined = $matches[3];
-      if (array_key_exists(4, $matches)) {
-        $combined .= $matches[4];
-      }
-      if (array_key_exists(6, $matches)) {
-        $combined .= $matches[6];
-      }
-
-      $scheme_string = preg_replace('!:' . $combined . '$!iu', '', $matches[0]);
-      $result['scheme'] = mb_split(':', $scheme_string);
-      unset($scheme_string);
-      unset($combined);
-
-      foreach ($result['scheme'] as &$s) {
-        $s = urldecode($s);
-      }
-      unset($s);
-    }
-
-
-    // process authority.
-    if (self::p_length_string($matches[3]) > 0) {
-      // rfc6854 designates multiple uris, separated by commas.
-      $authority = mb_split(',', $matches[3]);
-      foreach ($authority as $a) {
-        $sub_matches = array();
-        $sub_matched = preg_match('!^(//|/|)(([^@]+)@)*(.*)$!iu', $a, $sub_matches);
-
-        if ($sub_matched === FALSE || !isset($sub_matches[4])) {
-          $result['invalid'] = TRUE;
-
-          unset($sub_matches);
-          unset($sub_matched);
-          continue;
-        }
-
-
-        // process user information.
-        $information_matches = array();
-        if (preg_match('@^([=|!|$|&|\'|(|)|\*|\+|,|;|\w|\d|-|\.|_|~|%|\s]*)(:|)$@iu', $sub_matches[3], $information_matches) === FALSE || !isset($information_matches[1])) {
-          $result['invalid'] = TRUE;
-
-          unset($information_matches);
-          unset($sub_matches);
-          unset($sub_matched);
-          continue;
-        }
-
-        $authority_setting = array(
-          'type_path' => self::URI_PATH_THIS,
-          'type_host' => 0,
-          'user' => urldecode($information_matches[1]),
-          'host' => NULL,
-          'port' => NULL,
-        );
-        unset($information_matches);
-
-
-        // process host information.
-        if ($sub_matches[1] == '//') {
-          $authority_setting['type_path'] = self::URI_PATH_SITE;
-        }
-        elseif ($sub_matches[1] == '/') {
-          $authority_setting['type_path'] = self::URI_PATH_BASE;
-        }
-
-        $ipv6_matches = array();
-        if (preg_match('@^\[([^\]]+)\](:\d+$|$)@iu', $sub_matches[4], $ipv6_matches) !== FALSE && isset($ipv6_matches[1])) {
-          $authority_setting['type_host'] = self::URI_HOST_IPV6;
-
-          $ip = inet_pton($ipv6_matches[1]);
-          if ($ip === FALSE) {
-            $result['invalid'] = TRUE;
-
-            unset($ip);
-            unset($ipv6_matches);
-            continue;
-          }
-
-          $authority_setting['host'] = inet_ntop($ip);
-          unset($ip);
-
-          if (isset($ipv6_matches[2]) && self::p_length_string($ipv6_matches[2]) > 0) {
-            $authority_setting['port'] = (int) $ipv6_matches[2];
-          }
-
-          // @todo: ipvfuture is actually embedded inside of the the double brackets used by ipv6.
-          //        to support this, the ipv6 regex must be modified to check for the ipvfuture parameters.
-          // $authority_setting['type_host'] = self::URI_HOST_IPVX;
-          // '@v[\d|a|b|c|d|e|f]\.([=|!|$|&|\'|(|)|\*|\+|,|;|\w|\d|-|\.|_|~|%|:]*)@i'
-        }
-        unset($ipv6_matches);
-
-        $ipv4_matches = array();
-        if (is_null($authority_setting['host']) && preg_match('@(\d+\.\d+\.d+\.d+)(:(\d+)|)$@iu', $sub_matches[4], $ipv4_matches) !== FALSE && isset($ipv4_matches[1])) {
-          $authority_setting['type_host'] = self::URI_HOST_IPV4;
-
-          $ip = inet_pton($ipv4_matches[1]);
-          if ($ip === FALSE) {
-            $result['invalid'] = TRUE;
-
-            unset($ip);
-            unset($ipv4_matches);
-            continue;
-          }
-
-          $authority_setting['host'] = inet_ntop($ip);
-          unset($ip);
-
-          if (isset($ipv4_matches[3]) && self::p_length_string($ipv4_matches[3]) > 0) {
-            $authority_setting['port'] = (int) $ipv4_matches[3];
-          }
-        }
-        unset($ipv4_matches);
-
-        $ipv4_matches = array();
-        if (is_null($authority_setting['host']) && preg_match('@(\d+\.\d+\.d+\.d+)(:(\d+)|)$@iu', $sub_matches[4], $ipv4_matches) !== FALSE && isset($ipv4_matches[1])) {
-          $authority_setting['type_host'] = self::URI_HOST_IPV4;
-
-          $ip = inet_pton($ipv4_matches[1]);
-          if ($ip === FALSE) {
-            $result['invalid'] = TRUE;
-
-            unset($ip);
-            unset($ipv4_matches);
-            continue;
-          }
-
-          $authority_setting['host'] = inet_ntop($ip);
-          unset($ip);
-
-          if (isset($ipv4_matches[3]) && self::p_length_string($ipv4_matches[3]) > 0) {
-            $authority_setting['port'] = (int) $ipv4_matches[3];
-          }
-        }
-        unset($ipv4_matches);
-
-        $name_matches = array();
-        if (is_null($authority_setting['host']) && preg_match('@((=|\w|\d|-|\.|_|~|\!|$|&|\'|(|)|\*|\+|,|;)+)(:(\d+)|)$@iu', $sub_matches[4], $name_matches) !== FALSE && isset($name_matches[1])) {
-          $authority_setting['type_host'] = self::URI_HOST_NAME;
-          $authority_setting['host'] = $name_matches[2];
-
-          if (isset($name_matches[4]) && self::p_length_string($name_matches[4]) > 0) {
-            $authority_setting['port'] = (int) $name_matches[4];
-          }
-        }
-        unset($name_matches);
-
-        $result['authority'][] = $authority_setting;
-
-        unset($authority_setting);
-        unset($sub_matches);
-        unset($sub_matched);
-      }
-
-      unset($a);
-      unset($authority);
-    }
-
-
-    // process query.
-    if (array_key_exists(5, $matches) && self::p_length_string($matches[5]) > 0) {
-      $query_parts = mb_split(',', $matches[5]);
-
-      foreach ($query_parts as $qp) {
-        $qp_parts = mb_split('=', $qp, 2);
-
-        if (is_array($qp_parts) && isset($qp_parts[0])) {
-          $decoded = urldecode($qp_parts[0]);
-          if (isset($qp_parts[1])) {
-            $result['query'][$decoded] = urldecode($qp_parts[1]);
-          }
-          else {
-            $result['query'][$decoded] = NULL;
-          }
-          unset($decoded);
-        }
-      }
-      unset($qp);
-      unset($query_parts);
-    }
-
-
-    // process fragment.
-    if (array_key_exists(7, $matches) && self::p_length_string($matches[7]) > 0) {
-      $result['fragment'][] = urldecode($matches[7]);
-    }
-
-    unset($matches);
-*/
-    return $result;
-  }
-
-  /**
    * Decode and check that the given string is a valid entity tag such as with if-match (but do not test for weakness).
    *
    * Validation is done according to rfc7232.
@@ -7784,16 +7978,29 @@ class c_base_http extends c_base_rfc_string {
    *
    * @param string $token_name
    *   The string to sanitize as a token name.
+   * @param bool $lower_case
+   *   (optional) Force token to be lower-case.
+   *   There are some cases where the token case may need to remain untouched.
+   *   In such cases, set this to FALSE.
    *
    * @return string|bool
    *   A sanitized string is return on success.
    *   FALSE is returned on error or if the header name is invalid.
    */
-  private function p_prepare_token($token_name) {
-    $trimmed = preg_replace('/(^\s+)|(\s+$)/us', '', mb_strtolower($token_name));
-    if ($trimmed === FALSE) {
-      unset($trimmed);
-      return FALSE;
+  private function p_prepare_token($token_name, $lower_case = TRUE) {
+    if ($lower_case) {
+      $trimmed = preg_replace('/(^\s+)|(\s+$)/us', '', mb_strtolower($token_name));
+      if ($trimmed === FALSE) {
+        unset($trimmed);
+        return FALSE;
+      }
+    }
+    else {
+      $trimmed = preg_replace('/(^\s+)|(\s+$)/us', '', $token_name);
+      if ($trimmed === FALSE) {
+        unset($trimmed);
+        return FALSE;
+      }
     }
 
     $text = $this->pr_rfc_string_prepare($trimmed);
@@ -7824,13 +8031,24 @@ class c_base_http extends c_base_rfc_string {
    *   The header output array to make changes to.
    *
    * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Origin
    */
   private function p_prepare_header_response_access_control_allow_origin($header_name, &$header_output) {
     if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN, $this->response)) {
       return;
     }
 
-    // @todo
+    if ($this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN]['wildcard']) {
+      $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN] = $header_name . self::SEPARATOR_HEADER_NAME . '*';
+      return;
+    }
+
+    $combined = pr_rfc_string_combine_uri_array($this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN]);
+    if (is_string($combined)) {
+      $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN] = $header_name . self::SEPARATOR_HEADER_NAME . $combined;
+    }
+    unset($combined);
   }
 
   /**
@@ -7842,13 +8060,29 @@ class c_base_http extends c_base_rfc_string {
    *   The header output array to make changes to.
    *
    * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Expose-Headers
    */
   private function p_prepare_header_response_access_control_expose_headers($header_name, &$header_output) {
     if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_EXPOSE_HEADERS, $this->response)) {
       return;
     }
 
-    // @todo
+    $header_output[self::RESPONSE_ACCESS_CONTROL_EXPOSE_HEADERS] = $header_name . self::SEPARATOR_HEADER_NAME;
+
+    if (!empty($this->response[self::RESPONSE_ACCESS_CONTROL_EXPOSE_HEADERS])) {
+      $exposed_headers_array = $this->response[self::RESPONSE_ACCESS_CONTROL_EXPOSE_HEADERS];
+
+      reset($exposed_headers_array);
+      $exposed_header_name = array_shift($exposed_headers_array);
+      $header_output[self::RESPONSE_ACCESS_CONTROL_EXPOSE_HEADERS] .= $exposed_header_name;
+
+      foreach ($exposed_headers_array as $exposed_header_name) {
+        $header_output[self::RESPONSE_ACCESS_CONTROL_EXPOSE_HEADERS] .= ', ' . $exposed_header_name;
+      }
+      unset($exposed_headers_array);
+      unset($exposed_header_name);
+    }
   }
 
   /**
@@ -7860,13 +8094,72 @@ class c_base_http extends c_base_rfc_string {
    *   The header output array to make changes to.
    *
    * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Methods
    */
   private function p_prepare_header_response_access_control_allow_methods($header_name, &$header_output) {
     if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS, $this->response)) {
       return;
     }
 
-    // @todo
+    $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS] = $header_name . self::SEPARATOR_HEADER_NAME;
+
+    $allow_methods_array = $this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS];
+
+    reset($allow_methods_array);
+    $methods = array_shift($allow_methods_array);
+    $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS] .= $methods;
+
+    foreach ($allow_methods_array as $methods) {
+      $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS] .= ', ' ;
+
+      switch ($methods) {
+        case self::HTTP_METHOD_HEAD:
+          $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS] .= 'HEAD';
+          break;
+
+        case self::HTTP_METHOD_POST:
+          $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS] .= 'POST';
+          break;
+
+        case self::HTTP_METHOD_PUT:
+          $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS] .= 'PUT';
+          break;
+
+        case self::HTTP_METHOD_DELETE:
+          $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS] .= 'DELETE';
+          break;
+
+        case self::HTTP_METHOD_TRACE:
+          $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS] .= 'TRACE';
+          break;
+
+        case self::HTTP_METHOD_OPTIONS:
+          $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS] .= 'OPTIONS';
+          break;
+
+        case self::HTTP_METHOD_CONNECT:
+          $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS] .= 'CONNECT';
+          break;
+
+        case self::HTTP_METHOD_PATCH:
+          $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS] .= 'PATCH';
+          break;
+
+        case self::HTTP_METHOD_TRACK:
+          $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS] .= 'TRACK';
+          break;
+
+        case self::HTTP_METHOD_DEBUG:
+          $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS] .= 'DEBUG';
+          break;
+
+      }
+      $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS] .= ', ' . $methods;
+    }
+    unset($allow_methods_array);
+    unset($methods);
+
   }
 
   /**
@@ -7878,13 +8171,31 @@ class c_base_http extends c_base_rfc_string {
    *   The header output array to make changes to.
    *
    * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   * @see: https://www.w3.org/TR/CSP2/
+   * @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Headers
    */
   private function p_prepare_header_response_access_control_allow_headers($header_name, &$header_output) {
     if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS, $this->response)) {
       return;
     }
 
-    // @todo
+    $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS] = $header_name . self::SEPARATOR_HEADER_NAME;
+
+    if (empty($this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS])) {
+      return;
+    }
+
+    $allowed_headers_array = $this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS];
+
+    reset($allowed_headers_array);
+    $allowed_header_name = array_shift($allowed_headers_array);
+    $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS] .= $allowed_header_name;
+
+    foreach ($allowed_headers_array as $allowed_header_name) {
+      $header_output[self::RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS] .= ', ' . $allowed_header_name;
+    }
+    unset($allowed_headers_array);
+    unset($allowed_header_name);
   }
 
   /**
@@ -8285,71 +8596,53 @@ class c_base_http extends c_base_rfc_string {
       return;
     }
 
-    $header_output[self::RESPONSE_LINK] = $header_name . self::SEPARATOR_HEADER_NAME;
-
-    $uri = NULL;
-    if ($this->response[self::RESPONSE_LINK]['uri']['url']) {
-      if (!is_null($this->response[self::RESPONSE_LINK]['uri']['scheme'])) {
-        $uri .= $this->response[self::RESPONSE_LINK]['uri']['scheme'] . '://';
-      }
-
-      if (!is_null($this->response[self::RESPONSE_LINK]['uri']['authority'])) {
-        $uri .= $this->response[self::RESPONSE_LINK]['uri']['authority'] . '/';
-      }
-
-      if (!is_null($this->response[self::RESPONSE_LINK]['uri']['path'])) {
-        $uri .= $this->response[self::RESPONSE_LINK]['uri']['path'];
-      }
-
-      if (!is_null($this->response[self::RESPONSE_LINK]['uri']['query'])) {
-        $uri .= '?' . $this->response[self::RESPONSE_LINK]['uri']['query'];
-      }
-
-      if (!is_null($this->response[self::RESPONSE_LINK]['uri']['fragment'])) {
-        $uri .= '#' . $this->response[self::RESPONSE_LINK]['uri']['fragment'];
-      }
-    }
-    else {
-      if (!is_null($this->response[self::RESPONSE_LINK]['uri']['scheme'])) {
-        $uri .= $this->response[self::RESPONSE_LINK]['uri']['scheme'] . ':';
+    foreach ($this->response[self::RESPONSE_LINK] as $uris) {
+      $uri = $this->pr_rfc_string_combine_uri_array($uris['uri']);
+      if ($uri === FALSE) {
+        unset($uri);
+        unset($uris);
+        return;
       }
 
-      if (!is_null($this->response[self::RESPONSE_LINK]['uri']['path'])) {
-        $uri .= $this->response[self::RESPONSE_LINK]['uri']['path'];
+      if (!isset($header_output[self::RESPONSE_LINK])) {
+        $header_output[self::RESPONSE_LINK] = '';
       }
-    }
-
-    $header_output[self::RESPONSE_LINK] .= '<' . $uri . '>';
-    unset($uri);
 
-    if (!empty($this->response[self::RESPONSE_LINK]['parameters'])) {
-      $parameter_value = reset($this->response[self::RESPONSE_LINK]['parameters']);
-      $parameter_name = key($this->response[self::RESPONSE_LINK]['parameters']);
-      unset($this->response[self::RESPONSE_LINK]['parameters'][$parameter_name]);
-
-      if (is_null($parameter_value)) {
-        $parameters_string = $parameter_name;
-      }
-      else {
-        $parameters_string = $parameter_name . '=' . $parameter_value;
-      }
+      $header_output[self::RESPONSE_LINK] .= $header_name . self::SEPARATOR_HEADER_NAME;
+      $header_output[self::RESPONSE_LINK] .= '<' . $uri . '>';
+      unset($uri);
 
-      foreach($this->response[self::RESPONSE_LINK]['parameters'] as $parameter_name => $parameter_value) {
-        $parameters_string .= '; ';
+      if (!empty($uris['parameters'])) {
+        $parameter_value = reset($uris['parameters']);
+        $parameter_name = key($uris['parameters']);
+        unset($uris['parameters'][$parameter_name]);
 
+        $parameters_string = '; ';
         if (is_null($parameter_value)) {
           $parameters_string .= $parameter_name;
         }
         else {
           $parameters_string .= $parameter_name . '=' . $parameter_value;
         }
-      }
-      unset($parameter_name);
-      unset($parameter_value);
 
-      $header_output[self::RESPONSE_LINK] .= $parameters_string;
-      unset($parameters_string);
+        foreach($uris['parameters'] as $parameter_name => $parameter_value) {
+          $parameters_string .= '; ';
+
+          if (is_null($parameter_value)) {
+            $parameters_string .= $parameter_name;
+          }
+          else {
+            $parameters_string .= $parameter_name . '=' . $parameter_value;
+          }
+        }
+        unset($parameter_name);
+        unset($parameter_value);
+
+        $header_output[self::RESPONSE_LINK] .= $parameters_string;
+        unset($parameters_string);
+      }
     }
+    unset($uris);
   }
 
   /**
@@ -8787,9 +9080,9 @@ class c_base_http extends c_base_rfc_string {
       return;
     }
 
-    // in this case, a new header is created for every single entry..
+    // in this case, a new header is created for every single entry.
     $header_output[self::RESPONSE_X_UA_COMPATIBLE] = array();
-    foreach($header_output[self::RESPONSE_X_UA_COMPATIBLE] as $browser_name => $compatible_version) {
+    foreach($this->response[self::RESPONSE_X_UA_COMPATIBLE] as $browser_name => $compatible_version) {
         $header_output[self::RESPONSE_X_UA_COMPATIBLE][] = $browser_name . '=' . $compatible_version;
     }
     unset($browser_name);
@@ -9203,6 +9496,31 @@ class c_base_http extends c_base_rfc_string {
   }
 
   /**
+   * Prepare HTTP response headers that are boolean values represented by the words true/false.
+   *
+   * @param string $header_name
+   *   The HTTP header name, such as: 'Age'.
+   * @param array $header_output
+   *   The header output array to make changes to.
+   * @param int $code
+   *   The HTTP header code, such as:  self::RESPONSE_AGE.
+   */
+  private function p_prepare_header_response_boolean_value($header_name, &$header_output, $code) {
+    if (!array_key_exists($code, $this->response)) {
+      return;
+    }
+
+    $header_output[$code] = $header_name . self::SEPARATOR_HEADER_NAME;
+
+    if ($this->response[$code]) {
+      $header_output[$code] .= 'true';
+    }
+    else {
+      $header_output[$code] .= 'false';
+    }
+  }
+
+  /**
    * Prepare HTTP response header: date
    *
    * @param string $header_name
index f163bb2dc274a6a2f691b92d2ecd14dd6c0b9620..122e578ae471e822cc3d13db2daad1cabbb40ed0 100644 (file)
@@ -332,10 +332,9 @@ abstract class c_base_rfc_char extends c_base_return {
   /**
    * Check to see if character is: tchar68.
    *
-   * This doesn't directly exist, but is supplied for use with token68 in the same way tchar is used by token.
-   *
-   * tchar = Visible ASCII character codes: 43, 45->57, 65->90, 95, 97->122, 126
+   * tchar = Visible ASCII character codes: 43, 45->57, 65->90, 95, 97->122, 126.
    * tchar = UTF_8-2, UTF_8-3, UTF_8-4.
+   * tchar = ALPHA, DIGIT, '-', '.', '_', '~', '+', '/'.
    *
    * @param int $ordinal
    *   A code representing a single character to test.
@@ -770,7 +769,8 @@ abstract class c_base_rfc_char extends c_base_return {
   /**
    * Check to see if character is: unreserved.
    *
-   * unreserved = ASCII character codes 45, 46, 48->57, 65->90, 95, 97->122, 126
+   * unreserved = ASCII character codes 45, 46, 48->57, 65->90, 95, 97->122, 126.
+   *            = ALPHA, DIGIT, '-', '.', '_', '~'.
    *
    * @param int $ordinal
    *   A code representing a single character to test.
@@ -793,9 +793,10 @@ abstract class c_base_rfc_char extends c_base_return {
   }
 
   /**
-   * Check to see if character is: unreserved.
+   * Check to see if character is: reserved.
    *
-   * reserved = ASCII character codes 33, 35, 36, 38->44, 47, 58, 59, 61, 63, 64, 91, 93
+   * reserved = ASCII character codes 33, 35, 36, 38->44, 47, 58, 59, 61, 63, 64, 91, 93.
+   *          = ':',  '/',  '?',  '#',  '[',  ']',  '@', '!',  '$',  '&',  ''',  '(',  ')',  '*',  '+',  ',',  ';',  '='.
    *
    * @param int $ordinal
    *   A code representing a single character to test.
@@ -828,7 +829,8 @@ abstract class c_base_rfc_char extends c_base_return {
   /**
    * Check to see if character is: gen-delims.
    *
-   * gen-delims = ASCII character codes 35, 47, 58, 63, 64, 91, 93
+   * gen-delims = ASCII character codes 35, 47, 58, 63, 64, 91, 93.
+   *            = ':', '/', '?', '#', '[', ']', '@'.
    *
    * @param int $ordinal
    *   A code representing a single character to test.
@@ -853,7 +855,8 @@ abstract class c_base_rfc_char extends c_base_return {
   /**
    * Check to see if character is: sub-delims.
    *
-   * sub-delims = ASCII character codes 33, 36, 38->44, 59, 61
+   * sub-delims = ASCII character codes 33, 36, 38->44, 59, 61.
+   *            = '!', '$', '&', ''', '(', ')', '*', '+', ',', ';', '='.
    *
    * @param int $ordinal
    *   A code representing a single character to test.
@@ -880,6 +883,102 @@ abstract class c_base_rfc_char extends c_base_return {
   }
 
   /**
+   * Check to see if character is: pchar.
+   *
+   * sub-delims = ASCII character codes 33, 36->46, 48->59, 61, 64->90, 95, 97->122, 126.
+   *            = ALPHA, DIGIT, '-', '.', '_', '~', '!', '$', '&', ''', '(', ')', '*', '+', ',', ';', '='. ':', '@', '%'.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc3986#appendix-A
+   */
+  protected function pr_rfc_char_is_pchar($ordinal) {
+    if ($ordinal == c_base_ascii::EXCLAMATION) {
+      return TRUE;
+    }
+
+    if ($ordinal > c_base_ascii::HASH && $ordinal < c_base_ascii::SLASH_FORWARD) {
+      return TRUE;
+    }
+
+    if ($ordinal > c_base_ascii::SLASH_FORWARD && $ordinal < c_base_ascii::LESS_THAN) {
+      return TRUE;
+    }
+
+    if ($ordinal == c_base_ascii::EQUAL) {
+      return TRUE;
+    }
+
+    if ($ordinal > c_base_ascii::QUESTION_MARK && $ordinal < c_base_ascii::BRACKET_OPEN) {
+      return TRUE;
+    }
+
+    if ($ordinal == c_base_ascii::UNDERSCORE) {
+      return TRUE;
+    }
+
+    if ($ordinal > c_base_ascii::GRAVE && $ordinal < c_base_ascii::BRACE_OPEN) {
+      return TRUE;
+    }
+
+    if ($ordinal == c_base_ascii::TILDE) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: query (or fragment).
+   *
+   * query = ASCII character codes 33, 36->59, 61, 63->90, 95, 97->122, 126.
+   *       = ALPHA, DIGIT, '-', '.', '_', '~', '!', '$', '&', ''', '(', ')', '*', '+', ',', ';', '='. ':', '@', '%', '?', '/'.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc3986#appendix-A
+   */
+  protected function pr_rfc_char_is_query($ordinal) {
+    if ($ordinal == c_base_ascii::EXCLAMATION) {
+      return TRUE;
+    }
+
+    if ($ordinal > c_base_ascii::HASH && $ordinal < c_base_ascii::LESS_THAN) {
+      return TRUE;
+    }
+
+    if ($ordinal == c_base_ascii::EQUAL) {
+      return TRUE;
+    }
+
+    if ($ordinal > c_base_ascii::GREATER_THAN && $ordinal < c_base_ascii::BRACKET_OPEN) {
+      return TRUE;
+    }
+
+    if ($ordinal == c_base_ascii::UNDERSCORE) {
+      return TRUE;
+    }
+
+    if ($ordinal > c_base_ascii::GRAVE && $ordinal < c_base_ascii::BRACE_OPEN) {
+      return TRUE;
+    }
+
+    if ($ordinal == c_base_ascii::TILDE) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
    * Check to see if character is: UTF_8-1 (ASCII).
    *
    * The standard claims the following ranges:
index 870e0840455b0e4a335c22f3b774a5e2813746b8..be257712fa44e5abfadbb8d5d40b25d79388834b 100644 (file)
@@ -349,8 +349,6 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
    *   - 'text': A string containing the processed entity tag.
    *   - 'current': an integer representing the position the counter stopped at.
    *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
-   *
-   * @see: base_rfc_char::pr_rfc_char_qtext()
    */
   protected function pr_rfc_string_is_entity_tag($ordinals, $characters, $start = 0, $stop = NULL) {
     $result = array(
@@ -773,8 +771,10 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
   /**
    * Processes a string based on the rfc syntax: credentials.
    *
-   * A string that is a digit has the following syntax:
-   * - token [ 1*(ws) ( token68 / [ ( "," / token *(ws) "=" *(ws) ( token / quoted-string ) ) *( *(ws) "," [ *(ws) token *(ws) "=" *(ws) ( token / quoted-string ) ] ) ] ) ]
+   * A string that has the following syntax:
+   * - 1*(tchar) 1*(wsp) 1*(tchar) *(wsp) "=" *(wsp) ( 1*(tchar) / quoted-string ) *( *(wsp) "," *(wsp) 1*(tchar) *(wsp) "=" *(wsp) ( 1*(tchar) / quoted-string ) ).
+   *
+   * @todo: this entire function is incomplete, finish writing it.
    *
    * @param array $ordinals
    *   An array of integers representing each character of the string.
@@ -796,14 +796,12 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
    */
   protected function pr_rfc_string_is_credentials($ordinals, $characters, $start = 0, $stop = NULL) {
     $result = array(
-      'text' => NULL,
+      'scheme' => NULL,
+      'parameters' => array(),
       'current' => $start,
       'invalid' => FALSE,
     );
 
-    // @todo: this looks like a lot of work, so deal with this at some point in the future because this is a very low priority function.
-    $result['invalid'] = TRUE;
-/*
     if (is_null($stop)) {
       $stop = count($ordinals);
     }
@@ -812,6 +810,8 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
       return $result;
     }
 
+
+    // load the scheme.
     for (; $result['current'] < $stop; $result['current']++) {
       if (!array_key_exists($result['current'], $ordinals) || !array_key_exists($result['current'], $characters)) {
         // @fixme: should error be reported? do some debugging with this.
@@ -819,17 +819,146 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
         break;
       }
 
-      $code = $ordinals[$result['current']];
+      if ($this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+        // reached end of the scheme.
+        $result['current']++;
 
-      if (!$this->pr_rfc_char_is_digit($code)) {
+        $this->p_rfc_string_skip_past_whitespace($ordinals, $characters, $stop, $result);
+        if ($result['invalid']) {
+          return $result;
+        }
+
+        break;
+      }
+      elseif (!$this->pr_rfc_char_is_tchar($ordinals[$result['current']])) {
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      $result['scheme'] .= $characters[$result['current']];
+    }
+
+    if ($result['current'] >= $stop) {
+      $result['invalid'] = TRUE;
+      return $result;
+    }
+
+
+    // load the parameters.
+    for (; $result['current'] < $stop; $result['current']++) {
+      if (!array_key_exists($result['current'], $ordinals) || !array_key_exists($result['current'], $characters)) {
+        // @fixme: should error be reported? do some debugging with this.
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      $this->p_rfc_string_skip_past_whitespace($ordinals, $characters, $stop, $result);
+      if ($result['invalid']) {
+        return $result;
+      }
+
+      // load the parameter name.
+      $parameter_name = NULL;
+      for (; $result['current'] < $stop; $result['current']++) {
+        if (!array_key_exists($result['current'], $ordinals) || !array_key_exists($result['current'], $characters)) {
+          // @fixme: should error be reported? do some debugging with this.
+          $result['invalid'] = TRUE;
+          return $result;
+        }
+
+        elseif (!$this->pr_rfc_char_is_tchar($ordinals[$result['current']])) {
+          $result['invalid'] = TRUE;
+          return $result;
+        }
+
+        $parameter_name .= $characters[$result['current']];
+      }
+
+      $this->p_rfc_string_skip_past_whitespace($ordinals, $characters, $stop, $result);
+      if ($result['invalid']) {
+        return $result;
+      }
+
+      if ($result['current'] >= $stop) {
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      // a parameter name must be followed by an equal sign and then the parameter value..
+      if ($ordinals[$result['current']] != c_base_ascii::EQUAL) {
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      $this->p_rfc_string_skip_past_whitespace($ordinals, $characters, $stop, $result);
+      if ($result['invalid']) {
+        return $result;
+      }
+
+      if ($result['current'] >= $stop) {
         $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      // load the parameter value.
+      if ($ordinals[$result['current']] == c_base_ascii::QUOTE_DOUBLE) {
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          $result['invalid'] = TRUE;
+          return $result;
+        }
+
+        $parsed = $this->pr_rfc_string_is_quoted_string($ordinals, $characters, $result['current'], self::STOP_AT_CLOSING_CHARACTER);
+        $result['current'] = $parsed['current'];
+
+        if ($parsed['invalid']) {
+          unset($parsed);
+
+          $result['invalid'] = TRUE;
+          return $result;
+        }
+
+        $result['parameters'][$parameter_name] = $parsed['text'];
+        unset($parsed);
+      }
+      else {
+        for (; $result['current'] < $stop; $result['current']++) {
+          if (!array_key_exists($result['current'], $ordinals) || !array_key_exists($result['current'], $characters)) {
+            // @fixme: should error be reported? do some debugging with this.
+            $result['invalid'] = TRUE;
+            return $result;
+          }
+
+          if (!$this->pr_rfc_char_is_tchar($ordinals[$result['current']])) {
+            break;
+          }
+
+          $result['parameters'][$parameter_name] .= $characters[$result['current']];
+        }
+      }
+
+      $this->p_rfc_string_skip_past_whitespace($ordinals, $characters, $stop, $result);
+      if ($result['invalid']) {
+        return $result;
+      }
+
+      if ($result['current'] >= $stop) {
         break;
       }
 
-      $result['text'] .= $characters[$result['current']];
+
+      // A comma designates a new entry
+      if ($ordinals[$result['current']] == c_base_ascii::COMMA) {
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          $result['invalid'] = TRUE;
+          return $result;
+        }
+      }
+
+      $parameter_name = NULL;
     }
-    unset($code);
-*/
+
     return $result;
   }
 
@@ -1514,7 +1643,7 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
     krsort($result['choices']);
 
     // The NULL key should be the first key in the weight.
-    $this->p_prepend_array_value(NULL, $result['choices']);
+    $this->pr_prepend_array_value(NULL, $result['choices']);
 
     return $result;
   }
@@ -1545,7 +1674,6 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
    *   - 'current': an integer representing the position the counter stopped at.
    *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
    *
-   * @see: base_rfc_char::pr_rfc_char_tchar()
    * @see: https://tools.ietf.org/html/rfc2616#section-3.7
    */
   protected function pr_rfc_string_is_media_type($ordinals, $characters, $start = 0, $stop = NULL) {
@@ -1824,7 +1952,6 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
    *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
    *
    * @see: self::pr_rfc_string_is_valued_token_comma()
-   * @see: base_rfc_char::pr_rfc_char_tchar()
    * @see: base_rfc_char::pr_rfc_string_is_media_type()
    * @see: https://tools.ietf.org/html/rfc2616#section-3.7
    */
@@ -1850,7 +1977,6 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
       $code = $ordinals[$result['current']];
 
       if ($this->pr_rfc_char_is_wsp($code)) {
-        // @todo: handle whitespace between '='.
         if (is_null($token_name)) {
           continue;
         }
@@ -2026,7 +2152,6 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
    *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
    *
    * @see: self::pr_rfc_string_is_valued_token()
-   * @see: base_rfc_char::pr_rfc_char_tchar()
    * @see: base_rfc_char::pr_rfc_string_is_media_type()
    * @see: https://tools.ietf.org/html/rfc2616#section-3.7
    */
@@ -2052,7 +2177,6 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
       $code = $ordinals[$result['current']];
 
       if ($this->pr_rfc_char_is_wsp($code)) {
-        // @todo: handle whitespace between '='.
         if (is_null($token_name)) {
           continue;
         }
@@ -2203,10 +2327,10 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
    * - A simpler syntax will therefore be used.
    *
    * A valued_token has the following syntax:
-   * - 1*(*(ws) "," *(ws) token)
+   * - 1*(*(wsp) "," *(wsp) token)
    *
    * Original valued_token standard syntax:
-   * - *("," *(ws)) token *(*(ws) "," *(ws) token)
+   * - *("," *(wsp)) token *(*(wsp) "," *(wsp) token)
    *
    * @param array $ordinals
    *   An array of integers representing each character of the string.
@@ -2225,7 +2349,6 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
    *   - 'current': an integer representing the position the counter stopped at.
    *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
    *
-   * @see: base_rfc_char::pr_rfc_char_tchar()
    * @see: https://tools.ietf.org/html/rfc7230#appendix-B
    */
   protected function pr_rfc_string_is_commad_token($ordinals, $characters, $start = 0, $stop = NULL) {
@@ -2307,6 +2430,94 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
   }
 
   /**
+   * Processes a string based on the rfc syntax: path.
+   *
+   * A path has the following syntax:
+   * - *(ALPHA / DIGIT / "-" / "." / "_" / "~" / "%" HEXDIG HEXDIG / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" / ":" / "@" / "/" / "?")
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed or the closing character is found.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'text': A string containing the validated path.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: https://tools.ietf.org/html/rfc3986#section-3.4
+   */
+  protected function pr_rfc_string_is_path($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'text' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if ($code == c_base_ascii::PERCENT) {
+        // valid only if two hex digits immediately follow.
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          // this is invalid because it is cut off before 2 hex digits are required and none is found.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $code = $ordinals[$result['current']];
+        if (!$this->pr_rfc_char_is_hexdigit($ordinals[$result['current']])) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          // this is invalid because it is cut off before 2 hex digits are required and only one is found.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $code = $ordinals[$result['current']];
+        if (!$this->pr_rfc_char_is_hexdigit($ordinals[$result['current']])) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+      }
+      elseif (self::pr_rfc_char_is_pchar($code)) {
+        // do nothing, valid.
+      }
+      elseif ($code == c_base_asccii::SLASH_FORWARD) {
+        // do nothing, valid.
+      }
+      else {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $result['text'] .= $characters[$result['current']];
+    }
+    unset($code);
+
+    return $result;
+  }
+
+  /**
    * Processes a string based on the rfc syntax: query.
    *
    * This is also used by the rfc syntax: fragment.
@@ -2326,21 +2537,15 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
    *
    * @return array
    *   The processed information:
-   *   - 'address': A string containing the processed ip address.
-   *                When is_future is TRUE, this is an array containing:
-   *                - 'version': The ipfuture version.
-   *                - 'ip': The ip address.
-   *   - 'is_future': A boolean that when TRUE represents an ipvfuture address and when FALSE represents an ipv6 address.
+   *   - 'text': A string containing the validated query.
    *   - 'current': an integer representing the position the counter stopped at.
    *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
    *
-   * @see: base_rfc_char::pr_rfc_char_tchar()
-   * @see: https://tools.ietf.org/html/rfc2616#section-3.7
+   * @see: https://tools.ietf.org/html/rfc3986#section-3.4
    */
   protected function pr_rfc_string_is_query($ordinals, $characters, $start = 0, $stop = NULL) {
     $result = array(
-      'address' => NULL,
-      'is_future' => FALSE,
+      'text' => NULL,
       'current' => $start,
       'invalid' => FALSE,
     );
@@ -2353,7 +2558,53 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
       return $result;
     }
 
-    // @todo: finish writing this!
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if ($code == c_base_ascii::PERCENT) {
+        // valid only if two hex digits immediately follow.
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          // this is invalid because it is cut off before 2 hex digits are required and none is found.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $code = $ordinals[$result['current']];
+        if (!$this->pr_rfc_char_is_hexdigit($ordinals[$result['current']])) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          // this is invalid because it is cut off before 2 hex digits are required and only one is found.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $code = $ordinals[$result['current']];
+        if (!$this->pr_rfc_char_is_hexdigit($ordinals[$result['current']])) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+      }
+      elseif (self::pr_rfc_char_is_pchar($code)) {
+        // do nothing, valid.
+      }
+      elseif ($code == c_base_asccii::SLASH_FORWARD || $code == c_base_asccii::QUESTION_MARK) {
+        // do nothing, valid.
+      }
+      else {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $result['text'] .= $characters[$result['current']];
+    }
+    unset($code);
+
+    return $result;
   }
 
   /**
@@ -2385,7 +2636,6 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
    *   - 'current': an integer representing the position the counter stopped at.
    *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
    *
-   * @see: base_rfc_char::pr_rfc_char_tchar()
    * @see: https://tools.ietf.org/html/rfc2616#section-3.7
    */
   protected function pr_rfc_string_is_ip_literal($ordinals, $characters, $start = 0, $stop = NULL) {
@@ -2457,20 +2707,14 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
       for (; $result['current'] < $stop; $result['current']++) {
         $code = $ordinals[$result['current']];
 
-        if (self::pr_rfc_char_is_digit($code)) {
-          // do nothing, valid
-        }
-        elseif (self::pr_rfc_char_is_alpha($code)) {
-          // do nothing, valid
-        }
-        elseif (self::pr_rfc_char_is_unreserved($code)) {
-          // do nothing, valid
+        if (self::pr_rfc_char_is_unreserved($code)) {
+          // do nothing, valid.
         }
         elseif (self::pr_rfc_char_is_sub_delims($code)) {
-          // do nothing, valid
+          // do nothing, valid.
         }
         elseif ($code == c_base_ascii::COLON) {
-          // do nothing, valid
+          // do nothing, valid.
         }
         elseif ($code == c_base_ascii::BRACKET_CLOSE) {
           break;
@@ -2634,23 +2878,606 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
   }
 
   /**
-   * Effectively unshift a value onto a given array with a specified index.
+   * Decode and check that the given uri is valid.
+   *
+   * This does not decode the uri, it separates it into its individual parts.
+   *
+   * Validation is done according to rfc3986.
+   *
+   *   foo://example.com:8042/over/there?name=ferret#nose
+   *   \_/   \______________/\_________/ \_________/ \__/
+   *    |           |            |            |        |
+   *  scheme    authority       path        query   fragment
+   *    |   _____________________|__
+   *   / \ /                        \
+   *   foo:example:animal:ferret:nose
+   *
+   * The standard is misleading in its definition of path.
+   * First it says path can be path-abempty, path-absolute, path-noscheme, path-rootless, or path-empty.
+   * it then defines those as follows:
+   *   path-abempty:  begins with "/" or is empty
+   *   path-absolute: begins with "/" but not "//"
+   *   path-noscheme: begins with a non-colon segment
+   *   path-rootless: begins with a segment
+   *   path-empty:    zero characters
+   *
+   * path-abempty's definition includes a  '//', making path-absolute irrelevant/redundant.
+   * path-rootless's definition includes a colon, making path-noscheme irrelevant/redundant.
+   * path-empty's definition is inconsistent, why not say 'is empty' as with path-abempty?
+   *
+   * I am going to assume that path-abempty is meant to be defined as 'begins with "/" but not "//"' or 'is empty'.
+   *
+   * The standard also provides a regex example that violates their own rules:
+   * - Regex: ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
+   * - Their URI: http://www.ics.uci.edu/pub/ietf/uri/#Related
+   * - Example URI: example:relative:url/that:is:not/a:urn
+   *
+   * That example is syntatically valid for 'path-absolute', but has part of it turned into a scheme.
+   * While it is currently not popular to use ':' in a url path, it is valid according to the standard.
+   * Therefore, their example regex is non-conforming.
+   *
+   * In fact, as-written, the standard provides no means to distinguish between a scheme and a path.
+   * - Example Scheme: my_scheme:
+   * - Example Path:   my_path:
+   * Both of those are valid for their appropriate syntax.
+   *
+   * I believe that the way they wrote the standard is very mis-leading.
+   * path = path-abempty / path-absolute / path-noscheme / path-rootless / path-empty
+   * means that any one of those could be valid, thats an OR.
+   * but they are likely interpretting the '/' to be a separation betweem if/then conditionals that are not clearly defined.
+   *
+   * I am going to assume that what they mean is ':' is supported only in path IF a scheme is supplied.
+   * In this case the first colon separates the schema from everything else.
+   * Even with this interpretation, there are still problems because the following would still be syntatically valid:
+   * - Example URI: http://www.example.com/a:syntatically:valid:path
+   * If that example is what is necessary, then how does one make a valid relative uri for paths on that site!?
+   * - schemes are not allowed in relative paths, but that path still exists!
+   * - The standard supports these paths as absolute but does not support them as relative.
+   *
+   * For simplicity purposes, this function will violate the literal standard to follow what I am guessing to be the intended standard.
+   * If a colon is to appear in the path, it must be a URN and if so then it must have a scheme.
    *
-   * The NULL key should be the first key in the weight.
-   * PHP does not provide a way to preserve keys when merging arrays nor does PHP provide a way to unshift a value onto an array with a specific key.
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed.
    *
-   * @param $key
-   *   The index name to unshift the value onto the array as.
-   * @param array $array
-   *   The array to unshift onto.
+   * @return array
+   *   The processed information:
+   *   - 'scheme': The protocol string.
+   *   - 'authority': The domain string.
+   *   - 'path': The path string.
+   *   - 'query': An array of url arguments.
+   *   - 'fragment': The id string.
+   *   - 'url': A boolean that when TRUE means the string is a url and when FALSE the string is a urn.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: self::p_combine_uri_array()
+   * @see: self::pr_rfc_string_is_scheme()
+   * @see: self::pr_rfc_string_is_authority()
+   * @see: self::pr_rfc_string_is_path()
+   * @see: self::pr_rfc_string_is_query()
+   * @see: https://tools.ietf.org/html/rfc3986
    */
-  protected function p_prepend_array_value($key, &$array) {
-    if (!array_key_exists($key, $array)) {
-      return;
-    }
-
-    $value = $array[$key];
-    unset($array[$key]);
+  protected function pr_rfc_string_is_uri($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'scheme' => NULL,
+      'authority' => NULL,
+      'path' => NULL,
+      'query' => NULL,
+      'fragment' => NULL,
+      'url' => TRUE,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+
+    // handle path cases that begin with a forward slash because they are easy to identify.
+    if ($ordinals[$result['current']] == c_base_ascii::SLASH_FORWARD) {
+      $this->p_rfc_string_is_uri_path($ordinals, $characters, $stop, $result);
+      if ($result['invalid'] || $result['current'] >= $stop) {
+        return $result;
+      }
+
+
+      // check for query.
+      if ($ordinals[$result['current']] == c_base_ascii::QUESTION_MARK) {
+        // the first question mark is not recorded so skip past it before validating the fragment.
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          return $result;
+        }
+
+        $this->p_rfc_string_is_uri_query($ordinals, $characters, $stop, $result);
+        if ($result['invalid'] || $result['current'] >= $stop) {
+          return $result;
+        }
+      }
+
+
+      // check for fragment.
+      if ($ordinals[$result['current']] == c_base_ascii::HASH) {
+        // only the first hash is supported in the fragment (and it is not recorded) so skip past it before validating the fragment.
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          return $result;
+        }
+
+        $this->p_rfc_string_is_uri_fragment($ordinals, $characters, $stop, $result);
+        if ($result['invalid'] || $result['current'] >= $stop) {
+          return $result;
+        }
+      }
+
+      return $result;
+    }
+
+
+    // handle fragment cases first because they are easy to identify.
+    if ($ordinals[$result['current']] == c_base_ascii::HASH) {
+      for (; $result['current'] < $stop; $result['current']++) {
+        if (!array_key_exists($result['current'], $ordinals) || !array_key_exists($result['current'], $characters)) {
+          // @fixme: should error be reported? do some debugging with this.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $code = $ordinals[$result['current']];
+
+        // the syntax for query is identical to fragment.
+        if (!$this->pr_rfc_char_is_query($code)) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $result['fragment'] .= $characters[$result['current']];
+      }
+      unset($code);
+
+      return $result;
+    }
+
+
+    $not_scheme = FALSE;
+    $not_authority = FALSE;
+    $not_path = FALSE;
+    $processed_string = '';
+    for (; $result['current'] < $stop; $result['current']++) {
+      if (!array_key_exists($result['current'], $ordinals) || !array_key_exists($result['current'], $characters)) {
+        // @fixme: should error be reported? do some debugging with this.
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $code = $ordinals[$result['current']];
+
+      if ($this->pr_rfc_char_is_alpha($code)) {
+        // allowed in: scheme, authority, path
+      }
+      elseif ($this->pr_rfc_char_is_digit($code)) {
+        // allowed in: scheme, authority, path
+      }
+      elseif ($code == c_base_ascii::COLON) {
+        $not_path = TRUE;
+
+        if ($not_scheme) {
+          // must be an authority as per notes in the comments section of the function.
+          if ($not_authority) {
+            unset($not_scheme);
+            unset($not_authority);
+            unset($not_path);
+            unset($processed_string);
+
+            $result['invalid'] = TRUE;
+            return $result;
+          }
+        }
+        else {
+          // must be an scheme as per notes in the comments section of the function.
+          $not_authority = TRUE;
+        }
+      }
+      elseif ($code == c_base_ascii::PLUS || $code == c_base_ascii::MINUS || $code == c_base_ascii::PERIOD) {
+        // allowed in: scheme, authority, path
+      }
+      elseif ($code == c_base_ascii::AT || $code == c_base_ascii::SLASH_FORWARD) {
+        // allowed in: authority, path
+
+        $not_scheme = TRUE;
+      }
+      elseif ($this->pr_rfc_char_is_unreserved($code)) {
+        // allowed in: authority, path
+
+        $not_scheme = TRUE;
+      }
+      elseif ($code == c_base_ascii::BRACKET_OPEN) {
+        // allowed in: authority
+
+        $not_scheme = TRUE;
+        $not_path = TRUE;
+      }
+      else {
+        unset($not_scheme);
+        unset($not_authority);
+        unset($not_path);
+        unset($processed_string);
+
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      if (($not_scheme && $not_path) || ($not_scheme && $not_authority) || ($not_authority && $not_path)) {
+        break;
+      }
+
+      $processed_string .= $characters[$result['current']];
+    }
+    unset($code);
+
+    if ($result['current'] >= $stop) {
+      unset($not_scheme);
+      unset($not_authority);
+      unset($not_path);
+      unset($processed_string);
+
+      return $result;
+    }
+
+    if ($not_authority && $not_path) {
+      unset($not_scheme);
+      unset($not_authority);
+      unset($not_path);
+
+      $result['scheme'] = $processed_string;
+      unset($processed_string);
+
+      $result['current']++;
+      if ($result['current'] >= $stop) {
+        return $result;
+      }
+
+      // check to see if '/' immediately follows, if not then this is a urn.
+      $code = $ordinals[$result['current']];
+      if ($code == c_base_ascii::SLASH_FORWARD) {
+        unset($code);
+
+        // at this point it is known that this is a url instead of a urn.
+        $this->p_rfc_string_is_uri_path($ordinals, $characters, $stop, $result);
+        if ($result['invalid'] || $result['current'] >= $stop) {
+          return $result;
+        }
+
+        // check for query.
+        if ($ordinals[$result['current']] == c_base_ascii::QUESTION_MARK) {
+          // the first question mark is not recorded so skip past it before validating the fragment.
+          $result['current']++;
+          if ($result['current'] >= $stop) {
+            return $result;
+          }
+
+          $this->p_rfc_string_is_uri_query($ordinals, $characters, $stop, $result);
+          if ($result['invalid'] || $result['current'] >= $stop) {
+            return $result;
+          }
+        }
+
+        // check for fragment.
+        if ($ordinals[$result['current']] == c_base_ascii::HASH) {
+        // only the first hash is supported in the fragment (and it is not recorded) so skip past it before validating the fragment.
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          return $result;
+        }
+
+          $this->p_rfc_string_is_uri_fragment($ordinals, $characters, $stop, $result);
+          if ($result['invalid'] || $result['current'] >= $stop) {
+            return $result;
+          }
+        }
+
+        return $result;
+      }
+      unset($code);
+
+      // process path argument and if a single ':' is found, then this is a urn.
+      for (; $result['current'] < $stop; $result['current']++) {
+        if (!array_key_exists($result['current'], $ordinals) || !array_key_exists($result['current'], $characters)) {
+          unset($code);
+
+          // @fixme: should error be reported? do some debugging with this.
+          $result['invalid'] = TRUE;
+          return $result;
+        }
+
+        $code = $ordinals[$result['current']];
+
+        if ($code == c_base_ascii::HASH || $code == c_base_ascii::QUESTION_MARK) {
+          // found possible query or fragment.
+          $result['url'] = TRUE;
+          break;
+        }
+        elseif ($code == c_base_ascii::COLON) {
+          $result['url'] = FALSE;
+        }
+        elseif (!$this->pr_rfc_char_is_pchar($code)) {
+          unset($code);
+
+          $result['invalid'] = TRUE;
+          return $result;
+        }
+
+        $result['path'] .= $characters[$result['current']];
+      }
+      unset($code);
+
+      if ($result['current'] >= $stop) {
+        return $result;
+      }
+
+      // check for query.
+      if ($ordinals[$result['current']] == c_base_ascii::QUESTION_MARK) {
+        // the first question mark is not recorded so skip past it before validating the fragment.
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          return $result;
+        }
+
+        $this->p_rfc_string_is_uri_query($ordinals, $characters, $stop, $result);
+        if ($result['invalid'] || $result['current'] >= $stop) {
+          return $result;
+        }
+      }
+
+      // check for fragment.
+      if ($ordinals[$result['current']] == c_base_ascii::HASH) {
+        // only the first hash is supported in the fragment (and it is not recorded) so skip past it before validating the fragment.
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          return $result;
+        }
+
+        $this->p_rfc_string_is_uri_fragment($ordinals, $characters, $stop, $result);
+        if ($result['invalid'] || $result['current'] >= $stop) {
+          return $result;
+        }
+      }
+
+      return $result;
+    }
+    elseif ($not_scheme && $not_path) {
+      unset($not_scheme);
+      unset($not_authority);
+      unset($not_path);
+
+      $result['authority'] = $processed_string;
+      unset($processed_string);
+
+      $result['current']++;
+      if ($result['current'] >= $stop) {
+        return $result;
+      }
+
+      // check for authority.
+      $this->p_rfc_string_is_uri_authority($ordinals, $characters, $stop, $result);
+      if ($result['invalid'] || $result['current'] >= $stop) {
+        return $result;
+      }
+
+      // check for path.
+      $this->p_rfc_string_is_uri_path($ordinals, $characters, $stop, $result);
+      if ($result['invalid'] || $result['current'] >= $stop) {
+        return $result;
+      }
+
+      // check for query.
+      if ($ordinals[$result['current']] == c_base_ascii::QUESTION_MARK) {
+        // the first question mark is not recorded so skip past it before validating the fragment.
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          return $result;
+        }
+
+        $this->p_rfc_string_is_uri_query($ordinals, $characters, $stop, $result);
+        if ($result['invalid'] || $result['current'] >= $stop) {
+          return $result;
+        }
+      }
+
+      // check for fragment.
+      if ($ordinals[$result['current']] == c_base_ascii::HASH) {
+        // only the first hash is supported in the fragment (and it is not recorded) so skip past it before validating the fragment.
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          return $result;
+        }
+
+        $this->p_rfc_string_is_uri_fragment($ordinals, $characters, $stop, $result);
+        if ($result['invalid'] || $result['current'] >= $stop) {
+          return $result;
+        }
+      }
+    }
+    elseif ($not_scheme && $not_authority) {
+      unset($not_scheme);
+      unset($not_authority);
+      unset($not_path);
+
+      $result['path'] = $processed_string;
+      unset($processed_string);
+
+      $result['current']++;
+      if ($result['current'] >= $stop) {
+        return $result;
+      }
+
+      // check for path.
+      $this->p_rfc_string_is_uri_path($ordinals, $characters, $stop, $result);
+      if ($result['invalid'] || $result['current'] >= $stop) {
+        return $result;
+      }
+
+      // check for query.
+      if ($ordinals[$result['current']] == c_base_ascii::QUESTION_MARK) {
+        // the first question mark is not recorded so skip past it before validating the fragment.
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          return $result;
+        }
+
+        $this->p_rfc_string_is_uri_query($ordinals, $characters, $stop, $result);
+        if ($result['invalid'] || $result['current'] >= $stop) {
+          return $result;
+        }
+      }
+
+      // check for fragment.
+      if ($ordinals[$result['current']] == c_base_ascii::HASH) {
+        // only the first hash is supported in the fragment (and it is not recorded) so skip past it before validating the fragment.
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          return $result;
+        }
+
+        $this->p_rfc_string_is_uri_fragment($ordinals, $characters, $stop, $result);
+        if ($result['invalid'] || $result['current'] >= $stop) {
+          return $result;
+        }
+      }
+    }
+    unset($not_scheme);
+    unset($not_authority);
+    unset($not_path);
+    unset($processed_string);
+
+    $result['invalid'] = TRUE;
+
+    return $result;
+  }
+
+  /**
+   * Combine a uri array into a single uri.
+   *
+   * This does not validate the uri, it simply merges an array.
+   *
+   *   foo://example.com:8042/over/there?name=ferret#nose
+   *   \_/   \______________/\_________/ \_________/ \__/
+   *    |           |            |            |        |
+   *  scheme    authority       path        query   fragment
+   *    |   _____________________|__
+   *   / \ /                        \
+   *   urn:example:animal:ferret:nose
+   *
+   *
+   * @param array $uri_array
+   *   A url array with the following structure:
+   *   - 'scheme': The protocol string.
+   *   - 'authority': The domain string.
+   *   - 'path': The path string.
+   *   - 'query': An array of url arguments.
+   *   - 'fragment': The id string.
+   *
+   * @return string|bool
+   *   A combined url array on success.
+   *   FALSE is returned on error.
+   *
+   * @see: self::pr_rfc_string_is_uri()
+   * @see: https://tools.ietf.org/html/rfc3986
+   */
+  protected function pr_rfc_string_combine_uri_array($uri_array) {
+    if (!$uri_array['url']) {
+      // both scheme and path are required for urn.
+      if (!isset($uri_array['scheme']) || !isset($uri_array['path'])) {
+        return FALSE;
+      }
+
+      $combined .= $uri_array['scheme'] . ':' . $uri_array['path'];
+
+      return $combined;
+    }
+
+    $combined = NULL;
+    if (isset($uri_array['scheme'])) {
+      $combined .= $uri_array['scheme'] . ':';
+    }
+
+    if (isset($uri_array['authority'])) {
+      $combined .= $uri_array['authority'];
+    }
+
+    if (isset($uri_array['path'])) {
+      $combined .= $uri_array['path'];
+    }
+
+    if (!empty($uri_array['query'])) {
+      if (is_string($uri_array['query'])) {
+        $combined .= '?' . $uri_array['query'];
+      }
+      elseif (is_array($uri_array['query'])) {
+        $combined .= '?';
+
+        reset($uri_array['query']);
+        $query_name = key($uri_array['query']);
+        $query_value = $uri_array['query'][$query_name];
+        unset($uri_array['query'][$query_name]);
+
+        if (is_null($query_value)) {
+          $combined .= $query_name;
+        }
+        else {
+          $combined .= $query_name . '=' . $query_value;
+        }
+
+        foreach ($uri_array['query'] as $query_name => $query_value) {
+          if (is_null($query_value)) {
+            $combined .= $query_name;
+            continue;
+          }
+
+          $combined .= '&' . $query_name . '=' . $query_value;
+        }
+        unset($query_name);
+        unset($query_value);
+      }
+    }
+
+    if (isset($uri_array['fragment'])) {
+      $combined .= '#' . $uri_array['fragment'];
+    }
+
+    return $combined;
+  }
+
+  /**
+   * Effectively unshift a value onto a given array with a specified index.
+   *
+   * The NULL key should be the first key in the weight.
+   * PHP does not provide a way to preserve keys when merging arrays nor does PHP provide a way to unshift a value onto an array with a specific key.
+   *
+   * @param $key
+   *   The index name to unshift the value onto the array as.
+   * @param array $array
+   *   The array to unshift onto.
+   */
+  protected function pr_prepend_array_value($key, &$array) {
+    if (!array_key_exists($key, $array)) {
+      return;
+    }
+
+    $value = $array[$key];
+    unset($array[$key]);
 
     $new_array = array(
       $key => $value,
@@ -2664,4 +3491,279 @@ abstract class c_base_rfc_string extends c_base_rfc_char {
     $array = $new_array;
     unset($new_array);
   }
+
+  /**
+   * Helper function for pr_rfc_string_is_uri() to process: authority.
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $stop
+   *   The position in the arrays to stop checking.
+   * @param array $result
+   *   An array of return results used by the pr_rfc_string_is_uri().
+   *
+   * @see: self::pr_rfc_string_is_uri()
+   */
+  private function p_rfc_string_is_uri_authority(&$ordinals, &$characters, &$stop, &$result) {
+    for (; $result['current'] < $stop; $result['current']++) {
+      if (!array_key_exists($result['current'], $ordinals) || !array_key_exists($result['current'], $characters)) {
+        unset($code);
+
+        // @fixme: should error be reported? do some debugging with this.
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      $code = $ordinals[$result['current']];
+
+      if ($code == c_base_ascii::PERCENT) {
+        // valid only if two hex digits immediately follow.
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          // this is invalid because it is cut off before 2 hex digits are required and none is found.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $code = $ordinals[$result['current']];
+        if (!$this->pr_rfc_char_is_hexdigit($ordinals[$result['current']])) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          // this is invalid because it is cut off before 2 hex digits are required and only one is found.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $code = $ordinals[$result['current']];
+        if (!$this->pr_rfc_char_is_hexdigit($ordinals[$result['current']])) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+      }
+      elseif ($code == c_base_ascii::AT || $code == c_base_ascii::COLON) {
+        // this is valid.
+      }
+      elseif ($code == c_base_ascii::BRACKET_OPEN || $code == c_base_ascii::BRACKET_CLOSE) {
+        // this is valid.
+      }
+      elseif ($this->pr_rfc_char_is_unreserved($code)) {
+        // this is valid.
+      }
+      elseif ($this->pr_rfc_char_is_sub_delims($code)) {
+        // this is valid.
+      }
+      else {
+        unset($code);
+
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      $result['authority'] .= $characters[$result['current']];
+    }
+    unset($code);
+  }
+
+  /**
+   * Helper function for pr_rfc_string_is_uri() to process: path.
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $stop
+   *   The position in the arrays to stop checking.
+   * @param array $result
+   *   An array of return results used by the pr_rfc_string_is_uri().
+   *
+   * @see: self::pr_rfc_string_is_uri()
+   */
+  private function p_rfc_string_is_uri_path(&$ordinals, &$characters, &$stop, &$result) {
+    for (; $result['current'] < $stop; $result['current']++) {
+      if (!array_key_exists($result['current'], $ordinals) || !array_key_exists($result['current'], $characters)) {
+        unset($code);
+
+        // @fixme: should error be reported? do some debugging with this.
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      $code = $ordinals[$result['current']];
+
+      if ($code == c_base_ascii::HASH || $code == c_base_ascii::QUESTION_MARK) {
+        // found possible query or fragment.
+        break;
+      }
+      elseif ($code == c_base_ascii::SLASH_FORWARD) {
+        // this is valid.
+      }
+      elseif (!$this->pr_rfc_char_is_pchar($code)) {
+        unset($code);
+
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      $result['path'] .= $characters[$result['current']];
+    }
+    unset($code);
+  }
+
+  /**
+   * Helper function for pr_rfc_string_is_uri() to process: query.
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $stop
+   *   The position in the arrays to stop checking.
+   * @param array $result
+   *   An array of return results used by the pr_rfc_string_is_uri().
+   *
+   * @see: self::pr_rfc_string_is_uri()
+   */
+  private function p_rfc_string_is_uri_query(&$ordinals, &$characters, &$stop, &$result) {
+    $query_name = NULL;
+    $query_value = NULL;
+    $no_value = FALSE;
+
+    $result['query'] = array();
+    for (; $result['current'] < $stop; $result['current']++) {
+      if (!array_key_exists($result['current'], $ordinals) || !array_key_exists($result['current'], $characters)) {
+        unset($code);
+        unset($query_name);
+        unset($query_value);
+        unset($no_value);
+
+        // @fixme: should error be reported? do some debugging with this.
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      $code = $ordinals[$result['current']];
+
+      if ($code == c_base_ascii::HASH) {
+        // hash is not part of the query but does mark the end of the query as it is the start of the fragment.
+        break;
+      }
+      elseif ($code == c_base_ascii::AMPERSAND) {
+        // The '&' designates a new name and value, separate each individual value inside the array.
+        $result['query'][$query_name] = $query_value;
+
+        $query_name = NULL;
+        $query_value = NULL;
+        $no_value = FALSE;
+
+        continue;
+      }
+      elseif ($code == c_base_ascii::EQUAL) {
+        // The '=' designates a value for the current name.
+        if ($no_value || is_null($query_name)) {
+          $query_name .= $characters[$result['current']];
+          $no_value = TRUE;
+          continue;
+        }
+
+        $query_value = '';
+        continue;
+      }
+      elseif (!$this->pr_rfc_char_is_query($code)) {
+        unset($code);
+        unset($query_name);
+        unset($query_value);
+        unset($no_value);
+
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      if (is_null($query_value)) {
+        $query_name .= $characters[$result['current']];
+      }
+      else {
+        $query_value .= $characters[$result['current']];
+      }
+    }
+    unset($code);
+    unset($no_value);
+
+    $result['query'][$query_name] = $query_value;
+
+    unset($query_name);
+    unset($query_value);
+  }
+
+  /**
+   * Helper function for pr_rfc_string_is_uri() to process: fragment.
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $stop
+   *   The position in the arrays to stop checking.
+   * @param array $result
+   *   An array of return results used by the pr_rfc_string_is_uri().
+   *
+   * @see: self::pr_rfc_string_is_uri()
+   */
+  private function p_rfc_string_is_uri_fragment(&$ordinals, &$characters, &$stop, &$result) {
+    for (; $result['current'] < $stop; $result['current']++) {
+      if (!array_key_exists($result['current'], $ordinals) || !array_key_exists($result['current'], $characters)) {
+        unset($code);
+
+        // @fixme: should error be reported? do some debugging with this.
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      $code = $ordinals[$result['current']];
+
+      // the syntax for query is identical to fragment.
+      if (!$this->pr_rfc_char_is_query($code)) {
+        unset($code);
+
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      $result['fragment'] .= $characters[$result['current']];
+    }
+    unset($code);
+  }
+
+  /**
+   * Helper function for bypassing whitespaces.
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $stop
+   *   The position in the arrays to stop checking.
+   * @param array $result
+   *   An array of return results used by the pr_rfc_string_is_uri().
+   *
+   * @see: self::pr_rfc_string_is_uri()
+   */
+  private function p_rfc_string_skip_past_whitespace(&$ordinals, &$characters, &$stop, &$result) {
+    for (; $result['current'] < $stop; $result['current']++) {
+     if (!array_key_exists($result['current'], $ordinals) || !array_key_exists($result['current'], $characters)) {
+        // @fixme: should error be reported? do some debugging with this.
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+        break;
+      }
+    }
+  }
 }
index a5a76adf8de4de5004085fd41a44d6b483c5660a..c935ce06570b2a55d38cefca496d82cf57647907 100755 (executable)
                 $stuff['login'] = '';
               }
 
-              $user_data = get_user_data($database, $session->get_name()->get_value_exact(), $ldap_data);
+              $user_data = get_user_data($database, $session->get_name()->get_value_exact());
 
               $stuff['login'] .= ' - You are logged in as: ' . $session->get_name()->get_value_exact() . '<br>' . "\n";
               $stuff['login'] .= ' - Your user id is: ' . $session->get_id_user()->get_value_exact() . '<br>' . "\n";
 
     $connection_string = new c_base_connection_string();
     $connection_string->set_host('127.0.0.1');
-    $connection_string->set_port(5095);
+    $connection_string->set_port(5432);
     $connection_string->set_database('reservation');
     $connection_string->set_user($username);
     $connection_string->set_password($password);
       unset($response);
       return FALSE;
     }
-    unset($response);
 
     // an integer is expected to be returned by the socket.
     $response_packet = unpack('C', $response);
     $response_value = (int) $response_packet[1];
+    unset($response);
 
     // response codes as defined in the c source file:
     //    0 = no problems detected.
index 113f2655c2fa9b12d53aa5a5fb2b75561c2fcb85..d1d5b25a18369bd93d154e74d882038becacd823 100644 (file)
@@ -95,7 +95,8 @@
     #$http->set_response_last_modified(strtotime('now'));
     #$http->set_response_expires(strtotime('+30 minutes'));
     $http->set_response_pragma('no-cache');
-    #$http->set_response_vary('Date');
+    $http->set_response_vary('Host');
+    $http->set_response_vary('User-Agent');
     #$http->set_response_warning('1234 This site is under active development.');
 
     // finalize the content prior to sending headers to ensure header accuracy.