1 module hunt.http.codec.http.model.HttpCompliance; 2 3 import hunt.logging; 4 5 import hunt.http.codec.http.model.HttpComplianceSection; 6 7 import hunt.collection.HashMap; 8 import hunt.collection.Map; 9 10 import std.algorithm; 11 import std.array; 12 import std.string; 13 14 /** 15 * HTTP compliance modes for Jetty HTTP parsing and handling. 16 * A Compliance mode consists of a set of {@link HttpComplianceSection}s which are applied 17 * when the mode is enabled. 18 * <p> 19 * Currently the set of modes is an enum and cannot be dynamically extended, but future major releases may convert this 20 * to a class. To modify modes there are four custom modes that can be modified by setting the property 21 * <code>org.eclipse.jetty.http.HttpCompliance.CUSTOMn</code> (where 'n' is '0', '1', '2' or '3'), to a comma separated 22 * list of sections. The list should start with one of the following strings:<dl> 23 * <dt>0</dt><dd>No {@link HttpComplianceSection}s</dd> 24 * <dt>*</dt><dd>All {@link HttpComplianceSection}s</dd> 25 * <dt>RFC2616</dt><dd>The set of {@link HttpComplianceSection}s application to https://tools.ietf.org/html/rfc2616, 26 * but not https://tools.ietf.org/html/rfc7230</dd> 27 * <dt>RFC7230</dt><dd>The set of {@link HttpComplianceSection}s application to https://tools.ietf.org/html/rfc7230</dd> 28 * </dl> 29 * The remainder of the list can contain then names of {@link HttpComplianceSection}s to include them in the mode, or prefixed 30 * with a '-' to exclude thm from the mode. Note that Jetty's modes may have some historic minor differences from the strict 31 * RFC compliance, for example the <code>RFC2616_LEGACY</code> HttpCompliance is defined as 32 * <code>RFC2616,-FIELD_COLON,-METHOD_CASE_SENSITIVE</code>. 33 * <p> 34 * Note also that the {@link EnumSet} return by {@link HttpCompliance#sections()} is mutable, so that modes may 35 * be altered in code and will affect all usages of the mode. 36 */ 37 class HttpCompliance // TODO in Jetty-10 convert this enum to a class so that extra custom modes can be defined dynamically 38 { 39 /** 40 * A Legacy compliance mode to match jetty's behavior prior to RFC2616 and RFC7230. It only 41 * contains {@link HttpComplianceSection#METHOD_CASE_SENSITIVE} 42 */ 43 __gshared HttpCompliance LEGACY; 44 45 /** 46 * The legacy RFC2616 support, which incorrectly excludes 47 * {@link HttpComplianceSection#METHOD_CASE_SENSITIVE}, {@link HttpComplianceSection#FIELD_COLON} 48 */ 49 __gshared HttpCompliance RFC2616_LEGACY; 50 51 /** 52 * The strict RFC2616 support mode 53 */ 54 __gshared HttpCompliance RFC2616; 55 56 /** 57 * Jetty's current RFC7230 support, which incorrectly excludes {@link HttpComplianceSection#METHOD_CASE_SENSITIVE} 58 */ 59 __gshared HttpCompliance RFC7230_LEGACY; 60 61 /** 62 * The RFC7230 support mode 63 */ 64 __gshared HttpCompliance RFC7230; 65 66 // /** 67 // * Custom compliance mode that can be defined with System property <code>org.eclipse.jetty.http.HttpCompliance.CUSTOM0</code> 68 // */ 69 // deprecated("") 70 // CUSTOM0(sectionsByProperty("CUSTOM0")), 71 // /** 72 // * Custom compliance mode that can be defined with System property <code>org.eclipse.jetty.http.HttpCompliance.CUSTOM1</code> 73 // */ 74 // deprecated("") 75 // CUSTOM1(sectionsByProperty("CUSTOM1")), 76 // /** 77 // * Custom compliance mode that can be defined with System property <code>org.eclipse.jetty.http.HttpCompliance.CUSTOM2</code> 78 // */ 79 // deprecated("") 80 // CUSTOM2(sectionsByProperty("CUSTOM2")), 81 // /** 82 // * Custom compliance mode that can be defined with System property <code>org.eclipse.jetty.http.HttpCompliance.CUSTOM3</code> 83 // */ 84 // deprecated("") 85 // CUSTOM3(sectionsByProperty("CUSTOM3")); 86 87 // static string VIOLATIONS_ATTR = "org.eclipse.jetty.http.compliance.violations"; 88 89 90 // private static HttpComplianceSection[] sectionsByProperty(string property) { 91 // string s = System.getProperty(HttpCompliance.class.getName() + property); 92 // return sectionsBySpec(s == null ? "*" : s); 93 // } 94 95 static HttpComplianceSection[] sectionsBySpec(string spec) { 96 HttpComplianceSection[] sections; 97 string[] elements = spec.split(",").map!(a => strip(a)).array; 98 int i = 0; 99 100 switch (elements[i]) { 101 case "0": 102 sections = []; 103 i++; 104 break; 105 106 case "*": 107 i++; 108 sections = [HttpComplianceSection.CASE_INSENSITIVE_FIELD_VALUE_CACHE, HttpComplianceSection.FIELD_COLON, 109 HttpComplianceSection.FIELD_NAME_CASE_INSENSITIVE, HttpComplianceSection.NO_WS_AFTER_FIELD_NAME, 110 HttpComplianceSection.NO_FIELD_FOLDING, HttpComplianceSection.NO_HTTP_0_9]; 111 break; 112 113 case "RFC2616": 114 sections = [HttpComplianceSection.CASE_INSENSITIVE_FIELD_VALUE_CACHE, HttpComplianceSection.FIELD_COLON, 115 HttpComplianceSection.FIELD_NAME_CASE_INSENSITIVE, HttpComplianceSection.NO_WS_AFTER_FIELD_NAME]; 116 i++; 117 break; 118 119 case "RFC7230": 120 i++; 121 sections = [HttpComplianceSection.CASE_INSENSITIVE_FIELD_VALUE_CACHE, HttpComplianceSection.FIELD_COLON, 122 HttpComplianceSection.FIELD_NAME_CASE_INSENSITIVE, HttpComplianceSection.NO_WS_AFTER_FIELD_NAME, 123 HttpComplianceSection.NO_FIELD_FOLDING, HttpComplianceSection.NO_HTTP_0_9]; 124 break; 125 126 default: 127 sections = []; 128 break; 129 } 130 131 while (i < elements.length) { 132 string element = elements[i++]; 133 bool exclude = element.startsWith("-"); 134 if (exclude) 135 element = element[1..$]; 136 HttpComplianceSection section = HttpComplianceSection.valueOf(element); 137 if (section == HttpComplianceSection.Null) { 138 warningf("Unknown section '" ~ element ~ "' in HttpCompliance spec: " ~ spec); 139 continue; 140 } 141 if (exclude) 142 sections = sections.remove!(x => x == section)(); 143 else 144 sections ~= (section); 145 146 } 147 148 return sections; 149 } 150 151 private __gshared Map!(HttpComplianceSection, HttpCompliance) __required; 152 153 __gshared HttpCompliance[] values; 154 155 shared static this() { 156 157 LEGACY = new HttpCompliance(sectionsBySpec("0,METHOD_CASE_SENSITIVE")); 158 RFC2616_LEGACY = new HttpCompliance(sectionsBySpec("RFC2616,-FIELD_COLON,-METHOD_CASE_SENSITIVE")); 159 RFC2616 = new HttpCompliance(sectionsBySpec("RFC2616")); 160 RFC7230_LEGACY = new HttpCompliance(sectionsBySpec("RFC7230,-METHOD_CASE_SENSITIVE")); 161 RFC7230 = new HttpCompliance(sectionsBySpec("RFC7230")); 162 values ~= LEGACY; 163 values ~= RFC2616_LEGACY; 164 values ~= RFC2616; 165 values ~= RFC7230_LEGACY; 166 values ~= RFC7230; 167 168 __required = new HashMap!(HttpComplianceSection, HttpCompliance)(); 169 // LEGACY = new HttpCompliance(sectionsBySpec("0,METHOD_CASE_SENSITIVE")); 170 // LEGACY = new HttpCompliance(sectionsBySpec("0,METHOD_CASE_SENSITIVE")); 171 // LEGACY = new HttpCompliance(sectionsBySpec("0,METHOD_CASE_SENSITIVE")); 172 173 __required = new HashMap!(HttpComplianceSection, HttpCompliance)(); 174 175 foreach (HttpComplianceSection section ; HttpComplianceSection.values.byValue) { 176 foreach (HttpCompliance compliance ; HttpCompliance.values) { 177 if (compliance.sections().canFind(section)) { 178 __required.put(section, compliance); 179 break; 180 } 181 } 182 } 183 } 184 185 /** 186 * @param section The section to query 187 * @return The minimum compliance required to enable the section. 188 */ 189 static HttpCompliance requiredCompliance(HttpComplianceSection section) { 190 return __required.get(section); 191 } 192 193 private HttpComplianceSection[] _sections; 194 195 private this(HttpComplianceSection[] sections) { 196 _sections = sections; 197 } 198 199 /** 200 * Get the set of {@link HttpComplianceSection}s supported by this compliance mode. This set 201 * is mutable, so it can be modified. Any modification will affect all usages of the mode 202 * within the same {@link ClassLoader}. 203 * 204 * @return The set of {@link HttpComplianceSection}s supported by this compliance mode. 205 */ 206 HttpComplianceSection[] sections() { 207 return _sections; 208 } 209 210 }