1 /****************************************************************************** 2 3 HTTP version identifier constants and enumerator 4 5 Copyright: 6 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 7 All rights reserved. 8 9 License: 10 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 11 Alternatively, this file may be distributed under the terms of the Tango 12 3-Clause BSD License (see LICENSE_BSD.txt for details). 13 14 ******************************************************************************/ 15 16 module ocean.net.http.consts.HttpVersion; 17 18 19 import ocean.meta.types.Qualifiers; 20 import ocean.core.Verify; 21 import core.stdc.ctype: isdigit; 22 23 version (unittest) import ocean.core.Test; 24 25 /****************************************************************************** 26 27 HTTP version enumerator 28 29 ******************************************************************************/ 30 31 enum HttpVersion : ubyte 32 { 33 Undefined = 0, 34 v1_1, 35 v1_0 36 } 37 38 /****************************************************************************** 39 40 HTTP version identifier string constants and enumerator value association 41 42 ******************************************************************************/ 43 44 struct HttpVersionIds 45 { 46 /************************************************************************** 47 48 HTTP version identifier string constants 49 50 @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.1 51 52 **************************************************************************/ 53 54 enum istring[HttpVersion.max + 1] list = 55 [ 56 HttpVersion.v1_1: "HTTP/1.1", 57 HttpVersion.v1_0: "HTTP/1.0" 58 ]; 59 60 /************************************************************************** 61 62 Obtains the HTTP identifier string by version enumerator value. ver must 63 be a HttpVersion value different from HttpVersion.Undefined. 64 65 Params: 66 ver = HTTP version enumerator value 67 68 Returns: 69 HTTP version identifier string corresponding to val 70 71 **************************************************************************/ 72 73 static istring opIndex ( HttpVersion ver ) 74 { 75 verify(ver != 0, "no version id for HttpVersion.Undefined"); 76 verify(ver <= ver.max, "invalid HttpVersion enumerator value"); 77 78 return list[ver]; 79 } 80 81 /************************************************************************** 82 83 Obtains the HTTP version enumerator value by identifier string. 84 85 Params: 86 id = HTTP version identifier string 87 88 Returns: 89 Pointer to the HTTP version enumerator value corresponding to 90 identifier string or null if the name identifier does not match any 91 known HTTP version identifier string. 92 93 **************************************************************************/ 94 95 static HttpVersion* opBinaryRight (string op : "in") ( cstring id ) 96 { 97 return id.length? id in codes : null; 98 } 99 100 /************************************************************************** 101 102 Obtains the HTTP version enumerator value by identifier string. Does not 103 throw an exception. 104 105 Params: 106 id = HTTP version identifier string 107 108 Returns: 109 HTTP version enumerator value corresponding to identifier string or 110 HttpVersion.Undefined if the name string is unknown. 111 112 **************************************************************************/ 113 114 static HttpVersion opIndex ( cstring id ) 115 { 116 HttpVersion* code = opBinaryRight!("in")(id); 117 118 return code? *code : (*code).Undefined; 119 } 120 121 /************************************************************************** 122 123 Checks whether id has a valid syntax for a HTTP version identifier 124 string: 125 126 "HTTP" "/" 1*DIGIT "." 1*DIGIT 127 128 @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.1 129 130 Params: 131 id = HTTP version identifier string 132 133 Returns: 134 true if d has a valid syntax for a HTTP version identifier string 135 or false otherwise. 136 137 **************************************************************************/ 138 139 static bool validSyntax ( cstring id ) 140 { 141 enum prefix = "HTTP/"; 142 143 bool valid = id.length > prefix.length; 144 145 if (valid) 146 { 147 valid = id[0 .. prefix.length] == prefix; 148 } 149 150 if (valid) 151 { 152 size_t n_before_dot = 0; 153 154 foreach (i, c; id[prefix.length .. $]) 155 { 156 if (!isdigit(c)) 157 { 158 if (c == '.') 159 { 160 n_before_dot = i; 161 } 162 else 163 { 164 valid = false; 165 } 166 167 break; 168 } 169 } 170 171 valid &= n_before_dot != 0; 172 173 if (valid) 174 { 175 size_t after_dot = n_before_dot + prefix.length + 1; 176 177 valid &= id.length > after_dot; 178 179 if (valid) foreach (i, c; id[after_dot .. $]) 180 { 181 if (!isdigit(c)) 182 { 183 valid = false; 184 break; 185 } 186 } 187 } 188 } 189 190 return valid; 191 } 192 193 /************************************************************************** 194 195 Unittest for validSyntax() 196 197 **************************************************************************/ 198 199 unittest 200 { 201 test (validSyntax("HTTP/1.1")); 202 test (validSyntax("HTTP/1.23")); 203 test (validSyntax("HTTP/123.456")); 204 test (!validSyntax("HTTP/123456")); 205 test (!validSyntax("HTTP/.123456")); 206 test (!validSyntax("HTTP/1,1")); 207 test (!validSyntax("HTTP/1.")); 208 test (!validSyntax("HTTP/.1")); 209 test (!validSyntax("HTTP/.")); 210 test (!validSyntax("HTTP/")); 211 test (!validSyntax("")); 212 } 213 214 /************************************************************************** 215 216 HTTP version code enumerator value by name string 217 218 **************************************************************************/ 219 220 private static HttpVersion[istring] codes; 221 222 /************************************************************************** 223 224 Static constructor; populates this.codes 225 226 **************************************************************************/ 227 228 static this ( ) 229 { 230 foreach (i, str; list) 231 { 232 codes[str] = cast (HttpVersion) i; 233 } 234 235 codes.rehash; 236 } 237 } 238 239 240 unittest 241 { 242 static assert(HttpVersionIds.list[HttpVersion.v1_1] == "HTTP/1.1"); 243 static assert(HttpVersionIds.list[HttpVersion.v1_0] == "HTTP/1.0"); 244 245 test(!HttpVersionIds.list[HttpVersion.Undefined].length); 246 247 test(HttpVersionIds.list[HttpVersion.v1_1] == "HTTP/1.1"); 248 test(HttpVersionIds.list[HttpVersion.v1_0] == "HTTP/1.0"); 249 250 test(HttpVersionIds["HTTP/1.1"] == HttpVersion.v1_1); 251 test(HttpVersionIds["HTTP/1.0"] == HttpVersion.v1_0); 252 test(HttpVersionIds["SPAM"] == HttpVersion.Undefined); 253 test(HttpVersionIds[""] == HttpVersion.Undefined); 254 test(HttpVersionIds[null] == HttpVersion.Undefined); 255 256 HttpVersion* v = "HTTP/1.1" in HttpVersionIds; 257 test(v); 258 test(*v == (*v).v1_1); 259 260 v = "HTTP/1.0" in HttpVersionIds; 261 test(v); 262 test(*v == (*v).v1_0); 263 264 test(!("SPAM" in HttpVersionIds)); 265 test(!("" in HttpVersionIds)); 266 test(!(null in HttpVersionIds)); 267 }