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 }