= 400 ) { break; } // Redirect: resolve the Location header and follow. $location = wp_remote_retrieve_header( $response, 'location' ); if ( is_array( $location ) ) { $location = end( $location ); } if ( ! $location ) { break; } $next = LGBF_Link_Extractor::normalize( $location, $current ); if ( null === $next || $next === $current ) { break; } $redirect_count++; $current = $next; if ( $redirect_count > self::MAX_REDIRECTS ) { break; } } return array( 'status' => self::classify( $code, $redirect_count, $timed_out ), 'http_status' => $code, 'redirect_count' => $redirect_count, 'final_url' => ( $current !== $url ) ? $current : '', ); } /** * Classifies a check result into a 404Zen status. * * @param int $code Final HTTP status code. * @param int $redirect_count Redirect hops. * @param bool $timed_out Whether the request timed out. * @return string */ public static function classify( $code, $redirect_count, $timed_out = false ) { if ( 0 === $code ) { return $timed_out ? 'timeout' : 'broken'; } if ( $code >= 400 ) { return 'broken'; } if ( $code >= 300 ) { // Still a 3xx after MAX_REDIRECTS hops (redirect loop / too deep). return 'broken'; } // Success, but a chain of 2+ redirects is worth surfacing. if ( $redirect_count >= 2 ) { return 'redirect'; } return 'ok'; } /** * Performs a single, non-following HTTP request. * * @param string $url URL. * @param string $method HEAD or GET. * @return array|WP_Error */ private static function request( $url, $method ) { $args = array( 'method' => $method, 'timeout' => (int) LGBF_Settings::get( 'request_timeout' ), 'redirection' => 0, 'sslverify' => true, 'user-agent' => self::user_agent(), 'headers' => array( 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', ), ); if ( 'GET' === $method ) { // Never download more than 100 KB; the status line is all we need. $args['limit_response_size'] = 102400; } /** * Filters the HTTP request arguments used for link checks. * * @param array $args Request arguments. * @param string $url URL being checked. */ $args = apply_filters( 'lgbf_check_request_args', $args, $url ); return wp_remote_request( $url, $args ); } /** * Whether a WP_Error represents a timeout (vs. DNS failure etc.). * * @param WP_Error $error Error from the HTTP API. * @return bool */ private static function is_timeout( $error ) { $message = strtolower( $error->get_error_message() ); return false !== strpos( $message, 'timed out' ) || false !== strpos( $message, 'timeout' ); } /** * User agent string for outgoing checks. * * @return string */ private static function user_agent() { return 'Mozilla/5.0 (compatible; 404Zen/' . LGBF_VERSION . '; +' . home_url( '/' ) . ')'; } }