<?php /** * HTTP handling * * Handles all HTTP operations and detection. * * @package System * @author Pierre Lannoy <https://pierre.lannoy.fr/>. * @since 1.0.0 */ namespace Vibes\System; use Vibes\System\GeoIP; /** * Define the HTTP functionality. * * Handles all HTTP operations and detection. * * @package System * @author Pierre Lannoy <https://pierre.lannoy.fr/>. * @since 1.0.0 */ class Http { /** * The list of available verbs. * * @since 1.0.0 * @var array $verbs Maintains the verbs list. */ public static $verbs = [ 'get', 'head', 'post', 'put', 'delete', 'connect', 'options', 'trace', 'patch', 'unknown' ]; /** * The list of available contexts. * * @since 1.0.0 * @var array $contexts Maintains the contexts list. */ public static $contexts = [ 'inbound', 'outbound', 'unknown' ]; /** * The list of available schemes. * * @since 1.0.0 * @var array $schemes Maintains the schemes list. */ public static $schemes = [ 'http', 'https', 'unknown' ]; /** * The list of extended schemes. * * @since 1.0.0 * @var array $extended_schemes Maintains the extended schemes list. */ public static $extended_schemes = [ 'http', 'https', 'inline', 'unknown' ]; /** * The list of summarized HTTP codes. * * @since 2.3.0 * @var array $http_summary_codes Maintains the summary codes list. */ public static $http_summary_codes = [ 1, 2, 3, 4, 5, 9 ]; /** * The list of HTTP codes meaning success. * * @since 1.0.0 * @var array $http_success_codes Maintains the success codes list. */ public static $http_success_codes = [ 100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 304, 305, 306, 307, 308 ]; /** * The list of HTTP codes meaning effective pass. * * @since 1.0.0 * @var array $http_success_codes Maintains the effective pass codes list. */ public static $http_effective_pass_codes = [ 200, 201, 202, 203, 204, 205, 226 ]; /** * The list of HTTP codes meaning error. * * @since 1.0.0 * @var array $http_error_codes Maintains the error codes list. */ public static $http_error_codes = [ 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 422, 423, 424, 425, 426, 428, 429, 431, 444, 449, 450, 451, 494, 495, 496, 497, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 598, 599 ]; /** * The list of HTTP codes meaning global failure. * * @since 1.0.0 * @var array $http_failure_codes Maintains the failure codes list. */ public static $http_failure_codes = [ 0 ]; /** * The list of HTTP codes meaning quota problems. * * @since 1.0.0 * @var array $http_quota_codes Maintains the quota problems codes list. */ public static $http_quota_codes = [ 429, 509, 999 ]; /** * The list of possible "not" sub TLDs. * * @since 1.0.0 * @var array $not_sub_tlds Maintains the possible "not" sub TLDs list. */ public static $not_sub_tlds = [ 'wp' ]; /** * The list of HTTP status. * * @since 1.0.0 * @var array $http_status_codes Maintains the status list. */ public static $http_status_codes = [ 000 => 'Global timeout', 100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-Status', 208 => 'Already Reported', 226 => 'IM Used', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => '(Unused)', 307 => 'Temporary Redirect', 308 => 'Permanent Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', 418 => 'I\'m a teapot', 419 => 'Authentication Timeout', 420 => 'Enhance Your Calm', 422 => 'Unprocessable Entity', 423 => 'Locked', 424 => 'Method Failure', 425 => 'Unordered Collection', 426 => 'Upgrade Required', 428 => 'Precondition Required', 429 => 'Too Many Requests', 431 => 'Request Header Fields Too Large', 444 => 'No Response', 449 => 'Retry With', 450 => 'Blocked by Windows Parental Controls', 451 => 'Unavailable For Legal Reasons', 494 => 'Request Header Too Large', 495 => 'Cert Error', 496 => 'No Cert', 497 => 'HTTP to HTTPS', 499 => 'Client Closed Request', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported', 506 => 'Variant Also Negotiates', 507 => 'Insufficient Storage', 508 => 'Loop Detected', 509 => 'Bandwidth Limit Exceeded', 510 => 'Not Extended', 511 => 'Network Authentication Required', 598 => 'Network read timeout error', 599 => 'Network connect timeout error', 999 => 'Forbidden by quota manager', ]; /** * Set the plugin user agent. * * @return string The user agent to use. * @since 1.0.0 */ public static function user_agent() { return 'PerfOps One - ' . VIBES_PRODUCT_NAME . ' / ' . VIBES_VERSION . ' (https://perfops.one/' . VIBES_SLUG . ')'; } /** * Get the top domain of a full fqdn host. * * @param string $host The host. * @param boolean $resolve Optional. Try to resolve the host name. * @return string The top domain. * @since 1.0.0 */ public static function top_domain( $host, $resolve = true ) { if ( filter_var( $host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_IPV4 ) ) { if ( $resolve ) { if ( filter_var( $host, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE ) ) { return self::top_domain( gethostbyaddr( $host ), false ); } else { return $host; } } else { return $host; } } $parts = explode( '.', $host ); $middle_part = array_slice( $parts, -2, 1 ); $mid = $middle_part[0]; $subtld = ( strlen( $mid ) <= 3 ); if ( in_array( $mid, self::$not_sub_tlds, true ) ) { $subtld = false; } $slice = ( $subtld && ( count( $parts ) > 2 ) ) ? 3 : 2; $result = implode( '.', array_slice( $parts, ( 0 - $slice ), $slice ) ); return $result; } /** * Format a captured record. * * @param array $record The record to fromat. * @return array The record formatted. * @since 1.0.0 */ public static function format_record( $record ) { $result = []; $result['timestamp'] = $record['ts']; $result['site_id'] = 0; $result['bound'] = strtoupper( $record['context'] ); $result['authority'] = '-'; $result['scheme'] = '-'; $result['endpoint'] = '-'; $result['country'] = '-'; $result['verb'] = '-'; $result['code'] = 0; $result['message'] = '-'; $result['size'] = 0; $result['latency'] = 0; if ( ! in_array( $result['bound'], [ 'INBOUND', 'OUTBOUND' ], true ) ) { return $result; } $result['site_id'] = $record['site']; $result['authority'] = $record['authority']; $result['scheme'] = $record['scheme']; $result['endpoint'] = $record['endpoint']; $result['id'] = $record['id']; if ( 'INBOUND' === $result['bound'] ) { $geo_ip = new GeoIP(); $country = $geo_ip->get_iso3166_alpha2( $record['id'] ); if ( ! empty( $country ) ) { $result['country'] = $country; } } else { $geo_ip = new GeoIP(); $country = $geo_ip->get_iso3166_alpha2( $record['authority'] ); if ( ! empty( $country ) ) { $result['country'] = $country; } } $result['verb'] = strtoupper( $record['verb'] ); $result['code'] = $record['code']; if ( array_key_exists( $record['code'], self::$http_status_codes ) ) { $result['message'] = self::$http_status_codes[ $record['code'] ]; } $result['size'] = (int) ( ( $record['kb_out'] + $record['kb_in'] ) * 1024 ); $result['latency'] = $record['latency_min']; return $result; } }