1 module hunt.http.codec.http.model.HttpMethod; 2 3 import std.string; 4 import hunt.string; 5 import hunt.util.traits; 6 7 import hunt.container.ByteBuffer; 8 9 /** 10 */ 11 struct HttpMethod { 12 enum HttpMethod Null = HttpMethod("Null"); 13 enum HttpMethod GET = HttpMethod("GET"); 14 enum HttpMethod POST = HttpMethod("POST"); 15 enum HttpMethod HEAD = HttpMethod("HEAD"); 16 enum HttpMethod PUT = HttpMethod("PUT"); 17 enum HttpMethod OPTIONS = HttpMethod("OPTIONS"); 18 enum HttpMethod DELETE = HttpMethod("DELETE"); 19 enum HttpMethod TRACE = HttpMethod("TRACE"); 20 enum HttpMethod CONNECT = HttpMethod("CONNECT"); 21 enum HttpMethod MOVE = HttpMethod("MOVE"); 22 enum HttpMethod PROXY = HttpMethod("PROXY"); 23 enum HttpMethod PRI = HttpMethod("PRI"); 24 25 /* ------------------------------------------------------------ */ 26 27 /** 28 * Optimized lookup to find a method name and trailing space in a byte array. 29 * 30 * @param bytes Array containing ISO-8859-1 characters 31 * @param position The first valid index 32 * @param limit The first non valid index 33 * @return A HttpMethod if a match or null if no easy match. 34 */ 35 static HttpMethod lookAheadGet(byte[] bytes, int position, int limit) { 36 int length = limit - position; 37 if (length < 4) 38 return HttpMethod.Null; 39 switch (bytes[position]) { 40 case 'G': 41 if (bytes[position + 1] == 'E' && bytes[position + 2] == 'T' && bytes[position + 3] == ' ') 42 return GET; 43 break; 44 case 'P': 45 if (bytes[position + 1] == 'O' && bytes[position + 2] == 'S' && bytes[position + 3] == 'T' && length >= 5 && bytes[position + 4] == ' ') 46 return POST; 47 if (bytes[position + 1] == 'R' && bytes[position + 2] == 'O' && bytes[position + 3] == 'X' && length >= 6 && bytes[position + 4] == 'Y' && bytes[position + 5] == ' ') 48 return PROXY; 49 if (bytes[position + 1] == 'U' && bytes[position + 2] == 'T' && bytes[position + 3] == ' ') 50 return PUT; 51 if (bytes[position + 1] == 'R' && bytes[position + 2] == 'I' && bytes[position + 3] == ' ') 52 return PRI; 53 break; 54 case 'H': 55 if (bytes[position + 1] == 'E' && bytes[position + 2] == 'A' && bytes[position + 3] == 'D' && length >= 5 && bytes[position + 4] == ' ') 56 return HEAD; 57 break; 58 case 'O': 59 if (bytes[position + 1] == 'P' && bytes[position + 2] == 'T' && bytes[position + 3] == 'I' && length >= 8 && 60 bytes[position + 4] == 'O' && bytes[position + 5] == 'N' && bytes[position + 6] == 'S' && bytes[position + 7] == ' ') 61 return OPTIONS; 62 break; 63 case 'D': 64 if (bytes[position + 1] == 'E' && bytes[position + 2] == 'L' && bytes[position + 3] == 'E' && length >= 7 && 65 bytes[position + 4] == 'T' && bytes[position + 5] == 'E' && bytes[position + 6] == ' ') 66 return DELETE; 67 break; 68 case 'T': 69 if (bytes[position + 1] == 'R' && bytes[position + 2] == 'A' && bytes[position + 3] == 'C' && length >= 6 && 70 bytes[position + 4] == 'E' && bytes[position + 5] == ' ') 71 return TRACE; 72 break; 73 case 'C': 74 if (bytes[position + 1] == 'O' && bytes[position + 2] == 'N' && bytes[position + 3] == 'N' && length >= 8 && 75 bytes[position + 4] == 'E' && bytes[position + 5] == 'C' && bytes[position + 6] == 'T' && bytes[position + 7] == ' ') 76 return CONNECT; 77 break; 78 case 'M': 79 if (bytes[position + 1] == 'O' && bytes[position + 2] == 'V' && bytes[position + 3] == 'E' && length >= 5 && bytes[position + 4] == ' ') 80 return MOVE; 81 break; 82 83 default: 84 break; 85 } 86 return HttpMethod.Null; 87 } 88 89 /* ------------------------------------------------------------ */ 90 91 /** 92 * Optimized lookup to find a method name and trailing space in a byte array. 93 * 94 * @param buffer buffer containing ISO-8859-1 characters, it is not modified. 95 * @return A HttpMethod if a match or null if no easy match. 96 */ 97 static HttpMethod lookAheadGet(ByteBuffer buffer) { 98 if (buffer.hasArray()) 99 return lookAheadGet(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.arrayOffset() + buffer.limit()); 100 101 int l = buffer.remaining(); 102 if (l >= 4) { 103 string key = buffer.getString(0, l); 104 HttpMethod m = CACHE[key]; 105 if (m != HttpMethod.Null) { 106 int ml = cast(int)m.asString().length; 107 if (l > ml && buffer.get(buffer.position() + ml) == ' ') 108 return m; 109 } 110 } 111 return HttpMethod.Null; 112 } 113 114 /* ------------------------------------------------------------ */ 115 __gshared static HttpMethod[string] INSENSITIVE_CACHE; 116 __gshared static HttpMethod[string] CACHE; 117 118 static HttpMethod get(string name) 119 { 120 return CACHE.get(name, HttpMethod.Null); 121 } 122 123 static HttpMethod getInsensitive(string name) 124 { 125 return INSENSITIVE_CACHE.get(name.toLower(), HttpMethod.Null); 126 } 127 128 129 shared static this() { 130 foreach (HttpMethod method ; HttpMethod.values()) 131 { 132 INSENSITIVE_CACHE[method.toString().toLower()] = method; 133 CACHE[method.toString()] = method; 134 } 135 } 136 137 138 mixin GetConstantValues!(HttpMethod); 139 140 /* ------------------------------------------------------------ */ 141 private string _string; 142 // private ByteBuffer _buffer; 143 private byte[] _bytes; 144 145 /* ------------------------------------------------------------ */ 146 this(string s) { 147 _string = s; 148 _bytes = cast(byte[]) s.dup; // StringUtils.getBytes(s); 149 // _bytesColonSpace = cast(byte[])(s ~ ": ").dup; 150 } 151 152 153 bool isSame(string s) { 154 return s.length != 0 && std..string.icmp(_string, s) == 0; 155 } 156 157 string asString() { 158 return _string; 159 } 160 161 string toString() { 162 return _string; 163 } 164 165 /* ------------------------------------------------------------ */ 166 byte[] getBytes() { 167 return _bytes; 168 } 169 170 /* ------------------------------------------------------------ */ 171 // ByteBuffer asBuffer() { 172 // return _buffer.asReadOnlyBuffer(); 173 // } 174 175 /* ------------------------------------------------------------ */ 176 177 /** 178 * Converts the given string parameter to an HttpMethod 179 * 180 * @param method the string to get the equivalent HttpMethod from 181 * @return the HttpMethod or null if the parameter method is unknown 182 */ 183 static HttpMethod fromString(string method) { 184 string m = method.toLower(); 185 if(m in CACHE) 186 return CACHE[m]; 187 else 188 return HttpMethod.Null; 189 } 190 }