UNPKG

fingerprint-me-not

Version:

A powerful Node.js HTTP client with advanced TLS fingerprinting capabilities

125 lines 4.93 kB
import { STATUS_CODES } from 'http'; import { FingerPrintMeNotException } from './exceptions.js'; // Common binary content types const BINARY_CONTENT_TYPES = [ 'application/pdf', 'application/zip', 'application/octet-stream', 'image/', 'video/', 'audio/', 'application/msword', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument', 'application/x-binary', 'application/x-msdownload', 'application/x-executable' ]; function isBinaryContentType(contentType) { if (!contentType) return false; const lowerContentType = contentType.toLowerCase(); return BINARY_CONTENT_TYPES.some(binaryType => lowerContentType.includes(binaryType)); } export class Response { url; ok; status; reason; raiseForStatus; headers; cookies; content; text; json; isBinary; constructor(response, cookies) { this.url = response.target; this.ok = response.status >= 200 && response.status < 300; this.status = response.status; this.reason = STATUS_CODES[response.status]; this.raiseForStatus = () => { if (!this.ok) { throw new FingerPrintMeNotException(`Request failed with status code ${this.status}`); } }; this.headers = Object.fromEntries(Object.entries(response.headers).map(([key, value]) => [key, value.join(', ')])); this.cookies = cookies; // Determine if this is binary content const contentType = this.headers['Content-Type'] || this.headers['content-type'] || ''; this.isBinary = isBinaryContentType(contentType); // Handle binary vs text content appropriately // Check for body_len which indicates base64-encoded binary data from native code if (response.body_len && typeof response.body_len === 'number') { // Native code has sent base64-encoded binary data in response.body // response.body_len contains the original raw data length try { // Ensure the base64 string hasn't been corrupted by UTF-8 processing const base64Body = response.body || ''; // Validate base64 format and clean it if necessary const cleanBase64 = base64Body.replace(/[^A-Za-z0-9+/=]/g, ''); if (cleanBase64 !== base64Body) { console.warn('Warning: Base64 data was corrupted during UTF-8 processing. Attempting to clean.'); } this.content = Buffer.from(cleanBase64, 'base64'); this.text = ''; // Binary content should not be treated as text this.isBinary = true; // Verify the decoded length matches expected length if (this.content.length !== response.body_len) { console.warn(`Warning: Decoded binary length (${this.content.length}) doesn't match expected length (${response.body_len}). Data may be corrupted.`); } } catch (error) { console.error('Error decoding base64 binary data:', error); // Fallback to treating as text this.content = Buffer.from(response.body || '', 'utf-8'); this.text = response.body || ''; this.isBinary = false; } } else if (this.isBinary) { // Binary content detected by content-type but no body_len (legacy/fallback) console.warn(`Warning: Binary content detected (${contentType}) but no body_len field. Content may be truncated at null bytes.`); this.content = Buffer.from(response.body || '', 'binary'); this.text = response.body || ''; } else { // Text content this.content = Buffer.from(response.body || '', 'utf-8'); this.text = response.body || ''; } this.json = null; // Parse JSON if content type indicates JSON and it's not binary if (!this.isBinary && contentType.includes('application/json')) { try { this.json = JSON.parse(this.text); } catch (e) { // Ignore JSON parse errors } } } /** * Get the response content as a Buffer (recommended for binary data) */ getBuffer() { return this.content; } /** * Get the response content as text (only recommended for text content) */ getText() { if (this.isBinary) { console.warn('Warning: Attempting to get binary content as text. Use getBuffer() instead.'); } return this.text; } /** * Check if the response contains binary content */ isBinaryContent() { return this.isBinary; } } //# sourceMappingURL=response.js.map