Attic is a cozy space with lofty ambitions. attic.social
at main 164 lines 4.3 kB view raw
1// Copyright 2018-2026 the Deno authors. MIT license. 2/*! 3 * Adapted directly from negotiator at https://github.com/jshttp/negotiator/ 4 * which is licensed as follows: 5 * 6 * (The MIT License) 7 * 8 * Copyright (c) 2012-2014 Federico Romero 9 * Copyright (c) 2012-2014 Isaac Z. Schlueter 10 * Copyright (c) 2014-2015 Douglas Christopher Wilson 11 * 12 * Permission is hereby granted, free of charge, to any person obtaining 13 * a copy of this software and associated documentation files (the 14 * 'Software'), to deal in the Software without restriction, including 15 * without limitation the rights to use, copy, modify, merge, publish, 16 * distribute, sublicense, and/or sell copies of the Software, and to 17 * permit persons to whom the Software is furnished to do so, subject to 18 * the following conditions: 19 * 20 * The above copyright notice and this permission notice shall be 21 * included in all copies or substantial portions of the Software. 22 * 23 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 26 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 27 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 28 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 29 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 */ 31 32import { compareSpecs, isQuality, type Specificity } from "./common.ts"; 33 34interface EncodingSpecificity extends Specificity { 35 encoding?: string; 36} 37 38const simpleEncodingRegExp = /^\s*([^\s;]+)\s*(?:;(.*))?$/; 39 40function parseEncoding( 41 str: string, 42 i: number, 43): EncodingSpecificity | undefined { 44 const match = simpleEncodingRegExp.exec(str); 45 if (!match) { 46 return undefined; 47 } 48 49 const encoding = match[1]!; 50 let q = 1; 51 if (match[2]) { 52 const params = match[2].split(";"); 53 for (const param of params) { 54 const p = param.trim().split("="); 55 if (p[0] === "q" && p[1]) { 56 q = parseFloat(p[1]); 57 break; 58 } 59 } 60 } 61 62 return { encoding, o: undefined, q, i, s: undefined }; 63} 64 65function specify( 66 encoding: string, 67 spec: EncodingSpecificity, 68 i = -1, 69): Specificity | undefined { 70 if (!spec.encoding) { 71 return; 72 } 73 let s = 0; 74 if (spec.encoding.toLowerCase() === encoding.toLowerCase()) { 75 s = 1; 76 } else if (spec.encoding !== "*") { 77 return; 78 } 79 80 return { 81 i, 82 o: spec.i, 83 q: spec.q, 84 s, 85 }; 86} 87 88function parseAcceptEncoding(accept: string): EncodingSpecificity[] { 89 const accepts = accept.split(","); 90 const parsedAccepts: EncodingSpecificity[] = []; 91 let hasIdentity = false; 92 let minQuality = 1; 93 94 for (const [i, accept] of accepts.entries()) { 95 const encoding = parseEncoding(accept.trim(), i); 96 97 if (encoding) { 98 parsedAccepts.push(encoding); 99 hasIdentity = hasIdentity || !!specify("identity", encoding); 100 minQuality = Math.min(minQuality, encoding.q || 1); 101 } 102 } 103 104 if (!hasIdentity) { 105 parsedAccepts.push({ 106 encoding: "identity", 107 o: undefined, 108 q: minQuality, 109 i: accepts.length - 1, 110 s: undefined, 111 }); 112 } 113 114 return parsedAccepts; 115} 116 117function getEncodingPriority( 118 encoding: string, 119 accepted: Specificity[], 120 index: number, 121): Specificity { 122 let priority: Specificity = { o: -1, q: 0, s: 0, i: 0 }; 123 124 for (const s of accepted) { 125 const spec = specify(encoding, s, index); 126 127 if ( 128 spec && 129 (priority.s! - spec.s! || priority.q - spec.q || 130 priority.o! - spec.o!) < 131 0 132 ) { 133 priority = spec; 134 } 135 } 136 137 return priority; 138} 139 140/** Given an `Accept-Encoding` string, parse out the encoding returning a 141 * negotiated encoding based on the `provided` encodings otherwise just a 142 * prioritized array of encodings. */ 143export function preferredEncodings( 144 accept: string, 145 provided?: string[], 146): string[] { 147 const accepts = parseAcceptEncoding(accept); 148 149 if (!provided) { 150 return accepts 151 .filter(isQuality) 152 .sort(compareSpecs) 153 .map((spec) => spec.encoding!); 154 } 155 156 const priorities = provided.map((type, index) => 157 getEncodingPriority(type, accepts, index) 158 ); 159 160 return priorities 161 .filter(isQuality) 162 .sort(compareSpecs) 163 .map((priority) => provided[priorities.indexOf(priority)]!); 164}