Module: HttpMimic::SpaDetector
- Defined in:
- lib/http_mimic/spa_detector.rb
Constant Summary collapse
- EMPTY_MOUNT_REGEX =
Framework mount points that indicate an unhydrated SPA client shell
%r{ <div[^>]+id=["'](?:root|app|__next|__nuxt|svelte)["'][^>]*>\s*</div>| <app-root[^>]*>\s*</app-root>| <div[^>]+id=["']app["'][^>]*>\s*<!--\s*(?:app-content)?\s*-->\s*</div> }ix- NOSCRIPT_JS_REQUIRED_REGEX =
Noscript patterns requiring JavaScript execution
%r{ <noscript[^>]*>[\s\S]*?(?:enable\s+javascript|javascript\s+is\s+required|need\s+to\s+enable\s+javascript|requires\s+javascript)[\s\S]*?</noscript> }ix- GOOGLE_SHELL_REGEX =
Google Search Guard dynamic SPA shell indicators
%r{ (?:/httpservice/retry/enablejs|SG_REL|emsg=SG_REL|knitsail) }ix
Class Method Summary collapse
- .empty_mount_point?(body) ⇒ Boolean
- .google_serp_shell?(body) ⇒ Boolean
- .low_text_js_heavy_shell?(body) ⇒ Boolean
- .noscript_js_required?(body) ⇒ Boolean
-
.spa?(response) ⇒ Boolean
Returns true if the HTTP response represents an unhydrated Single Page Application (SPA) shell.
Class Method Details
.empty_mount_point?(body) ⇒ Boolean
60 61 62 |
# File 'lib/http_mimic/spa_detector.rb', line 60 def empty_mount_point?(body) body.match?(EMPTY_MOUNT_REGEX) end |
.google_serp_shell?(body) ⇒ Boolean
68 69 70 71 72 73 |
# File 'lib/http_mimic/spa_detector.rb', line 68 def google_serp_shell?(body) return false unless body.match?(GOOGLE_SHELL_REGEX) # If it has the Google shell indicators AND lacks rendered organic result elements !body.include?('<h3 class=') && !body.include?('id="search"') && !body.include?('id="rso"') end |
.low_text_js_heavy_shell?(body) ⇒ Boolean
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/http_mimic/spa_detector.rb', line 75 def low_text_js_heavy_shell?(body) return false if body.length > 25_000 # Larger HTML files typically have SSR content # Check if there are script tags script_count = body.scan(/<script/i).size return false if script_count.zero? # Strip all HTML tags, scripts, and styles to get raw visible text length visible_text = body .gsub(/<script[\s\S]*?<\/script>/i, '') .gsub(/<style[\s\S]*?<\/style>/i, '') .gsub(/<[^>]+>/, ' ') .gsub(/\s+/, ' ') .strip # If visible text is virtually nonexistent but page has multiple scripts visible_text.length < 80 && script_count >= 2 end |
.noscript_js_required?(body) ⇒ Boolean
64 65 66 |
# File 'lib/http_mimic/spa_detector.rb', line 64 def noscript_js_required?(body) body.match?(NOSCRIPT_JS_REQUIRED_REGEX) end |
.spa?(response) ⇒ Boolean
Returns true if the HTTP response represents an unhydrated Single Page Application (SPA) shell
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/http_mimic/spa_detector.rb', line 27 def spa?(response) return false unless response && response.success? return false if response.respond_to?(:binary?) && response.binary? content_type = response.headers['content-type'].to_s.downcase return false unless content_type.empty? || content_type.include?('text/html') || content_type.include?('application/xhtml') body = response.body.to_s return false if body.strip.empty? # 1. Google Dynamic SERP / Search Guard JS Shell if google_serp_shell?(body) return true end # 2. Modern SPA framework empty mount points (React, Vue, Angular, Next, Nuxt, Svelte) if empty_mount_point?(body) return true end # 3. Noscript requirement message if noscript_js_required?(body) return true end # 4. Small body with multiple scripts and virtually no visible text if low_text_js_heavy_shell?(body) return true end false end |