1 module hunt.http.HttpVersion;
2 
3 import hunt.io.ByteBuffer;
4 import hunt.Exceptions;
5 import hunt.util.ObjectUtils;
6 
7 import std.ascii;
8 import std.string;
9 
10 
11 struct HttpVersion {
12 	enum HttpVersion Null = HttpVersion("null", 0);
13 	enum HttpVersion HTTP_0_9 = HttpVersion("HTTP/0.9", 9);
14 	enum HttpVersion HTTP_1_0 = HttpVersion("HTTP/1.0", 10);
15 	enum HttpVersion HTTP_1_1 = HttpVersion("HTTP/1.1", 11);
16 	enum HttpVersion HTTP_2 = HttpVersion("HTTP/2.0", 20);
17 
18 	private __gshared HttpVersion[string] CACHE;
19 
20 	shared static this() {
21 		foreach (HttpVersion ver; HttpVersion.values())
22 			CACHE[ver.toString()] = ver;
23 	}
24 
25 	mixin ValuesMemberTempate!(HttpVersion);
26 
27 	/**
28 	 * Optimized lookup to find a HTTP Version and whitespace in a byte array.
29 	 * 
30 	 * @param bytes
31 	 *            Array containing ISO-8859-1 characters
32 	 * @param position
33 	 *            The first valid index
34 	 * @param limit
35 	 *            The first non valid index
36 	 * @return A HttpMethod if a match or null if no easy match.
37 	 */
38 	static HttpVersion lookAheadGet(byte[] bytes, int position, int limit) {
39 		int length = limit - position;
40 		if (length < 9)
41 			return HttpVersion.Null;
42 
43 		if (bytes[position + 4] == '/' && bytes[position + 6] == '.'
44 				&& std.ascii.isWhite(cast(char) bytes[position + 8])
45 				&& ((bytes[position] == 'H' && bytes[position + 1] == 'T'
46 					&& bytes[position + 2] == 'T' && bytes[position + 3] == 'P') || (bytes[position] == 'h'
47 					&& bytes[position + 1] == 't' && bytes[position + 2] == 't'
48 					&& bytes[position + 3] == 'p'))) {
49 			switch (bytes[position + 5]) {
50 			case '1':
51 				switch (bytes[position + 7]) {
52 				case '0':
53 					return HTTP_1_0;
54 				case '1':
55 					return HTTP_1_1;
56 				default:
57 					break;
58 				}
59 				break;
60 			case '2':
61 				if (bytes[position + 7] == '0') {
62 					return HTTP_2;
63 				}
64 				break;
65 
66 			default:
67 				break;
68 			}
69 		}
70 
71 		return HttpVersion.Null;
72 	}
73 
74 	/**
75 	 * Optimised lookup to find a HTTP Version and trailing white space in a
76 	 * byte array.
77 	 * 
78 	 * @param buffer
79 	 *            buffer containing ISO-8859-1 characters
80 	 * @return A HttpVersion if a match or null if no easy match.
81 	 */
82 	static HttpVersion lookAheadGet(ByteBuffer buffer) {
83 		if (buffer.hasArray())
84 			return lookAheadGet(buffer.array(), buffer.arrayOffset() + buffer.position(),
85 					buffer.arrayOffset() + buffer.limit());
86 		return HttpVersion.Null;
87 	}
88 
89 	private string _string;
90 	private byte[] _bytes;
91 	// private ByteBuffer _buffer;
92 	private int _version;
93 
94 	this(string s, int ver) {
95 		_string = s;
96 		_bytes = cast(byte[]) s.dup;
97 		// _buffer = BufferUtils.toBuffer(_bytes);
98 		_version = ver;
99 	}
100 
101 	byte[] toBytes() {
102 		return _bytes;
103 	}
104 
105 	// ByteBuffer toBuffer() {
106 	// 	return _buffer.asReadOnlyBuffer();
107 	// }
108 
109 	int getVersion() {
110 		return _version;
111 	}
112 
113 	bool isSame(string s) {
114 		return s.length != 0 && std..string.icmp(_string, s) == 0;
115 	}
116 
117 	string asString() {
118 		return _string;
119 	}
120 
121 	string toString() {
122 		return _string;
123 	}
124 
125 	int opCmp(ref const HttpVersion h) const {
126 		if (_version > h._version)
127 			return 1;
128 		else if (_version == h._version)
129 			return 0;
130 		else
131 			return -1;
132 	}
133 
134 	int opCmp(const HttpVersion h) const {
135 		if (_version > h._version)
136 			return 1;
137 		else if (_version == h._version)
138 			return 0;
139 		else
140 			return -1;
141 	}
142 
143 	/**
144 	 * Case insensitive fromString() conversion
145 	 * 
146 	 * @param version
147 	 *            the string to convert to enum constant
148 	 * @return the enum constant or null if version unknown
149 	 */
150 	static HttpVersion fromString(string ver) {
151 		return CACHE.get(ver, HttpVersion.Null);
152 	}
153 
154 	static HttpVersion fromVersion(int ver) {
155 		switch (ver) {
156 		case 9:
157 			return HttpVersion.HTTP_0_9;
158 		case 10:
159 			return HttpVersion.HTTP_1_0;
160 		case 11:
161 			return HttpVersion.HTTP_1_1;
162 		case 20:
163 			return HttpVersion.HTTP_2;
164 		default:
165 			throw new IllegalArgumentException("");
166 		}
167 	}
168 }