1 /**
2  * Tests
3  * 
4  * Copyright:
5  * (C) 2012-2015 Tatsuhiro Tsujikawa
6  * (C) 2014-2015 Etienne Cimon
7  *
8  * License: 
9  * Distributed under the terms of the MIT license with an additional section 1.2 of the curl/libcurl project. 
10  * Consult the provided LICENSE.md file for details
11  */
12 module libhttp2.tests;
13 
14 import core.stdc.stdlib;
15 import core.stdc.string;
16 import libhttp2.constants;
17 import libhttp2.types;
18 import libhttp2.helpers;
19 import libhttp2.inflater;
20 import libhttp2.deflater;
21 import libhttp2.session;
22 import libhttp2.stream;
23 import libhttp2.frame;
24 import libhttp2.huffman;
25 import libhttp2.buffers;
26 import std.algorithm : min;
27 
28 struct HeaderFields
29 {
30 	HeaderField[] opSlice() 
31 	{
32 		return hfa_raw[0 .. length];
33 	}
34 
35 	HeaderField[] opSlice(size_t i, size_t j) {
36 		assert(j <= length);
37 		return hfa_raw[i .. j];
38 	}
39 
40 	size_t opDollar() const { return length; }
41 
42 	void add(HeaderField hf) {
43 		HeaderField* hfp = hfa_raw.ptr + length;
44 		length++;
45 		if (hf.name.length > 0) 
46 			hfp.name = Mem.copy(hf.name);
47 		
48 		if (hf.value.length > 0)
49 			hfp.value = Mem.copy(hf.value);
50 
51 		hfp.flag = hf.flag;
52 	}
53 
54 	void reset() {
55 		size_t i;
56 		for (i = 0; i < length; ++i) {
57 			if (hfa_raw[i].name) 
58 				Mem.free(hfa_raw[i].name);
59 			if (hfa_raw[i].value) 
60 				Mem.free(hfa_raw[i].value);
61 		}
62 		hfa_raw.destroy();
63 		length = 0;
64 	}
65 
66 	/// returns the amount of headers added to hfa
67 	int inflate(ref Inflater inflater, Buffers bufs, size_t offset) 
68 	{
69 		int rv;
70 		HeaderField hf;
71 		InflateFlag inflate_flag;
72 		Buffers.Chain ci;
73 		Buffer* buf;
74 		Buffer bp;
75 		bool is_final;
76 		int processed;
77 		
78 		for (ci = bufs.head; ci; ci = ci.next) {
79 			buf = &ci.buf;
80 			is_final = buf.length == 0 || !ci.next;
81 			bp = *buf;
82 
83 			if (offset) {
84 				int n;
85 				
86 				n = min(cast(int)offset, bp.length);
87 				bp.pos += n;
88 				offset -= n;
89 			}
90 
91 			for (;;) {
92 				inflate_flag = InflateFlag.NONE;
93 				rv = inflater.inflate(hf, inflate_flag, bp[], is_final);
94 				if (rv < 0)
95 					return rv;
96 				
97 				bp.pos += rv;
98 				processed += rv;
99 				if (inflate_flag & InflateFlag.EMIT) 
100 					add(hf);
101 				if (inflate_flag & InflateFlag.FINAL)
102 					break;
103 			}
104 		}
105 		inflater.endHeaders();
106 		
107 		return processed;
108 	}
109 
110 	HeaderField[256] hfa_raw;
111 	size_t length;
112 }
113 
114 int compareBytes(in string a, in string b) {
115 	int rv;
116 	
117 	if (a.length == b.length) {
118 		return memcmp(a.ptr, b.ptr, a.length);
119 	}
120 	
121 	if (a.length < b.length) {
122 		rv = memcmp(a.ptr, b.ptr, a.length);
123 		
124 		if (rv == 0) {
125 			return -1;
126 		}
127 		
128 		return rv;
129 	}
130 	
131 	rv = memcmp(a.ptr, b.ptr, b.length);
132 	
133 	if (rv == 0) {
134 		return 1;
135 	}
136 	
137 	return rv;
138 }
139 
140 extern(C) @system
141 int compareHeaderFields(scope const(void *) lhs, scope const(void *) rhs) {
142 	const HeaderField a = *cast(HeaderField*)lhs;
143 	const HeaderField b = *cast(HeaderField*)rhs;
144 	int rv;
145 	
146 	rv = compareBytes(a.name, b.name);
147 	
148 	if (rv == 0) {
149 		return compareBytes(a.value, b.value);
150 	}
151 	
152 	return rv;
153 }
154 
155 void sort(HeaderField[] hfa) {
156 	qsort(cast(void*)hfa.ptr, hfa.length, HeaderField.sizeof, &compareHeaderFields);
157 }
158 
159 void packHeaders(Buffers bufs, ref Deflater deflater, int stream_id, FrameFlags flags, in HeaderField[] hfa)
160 {
161 	HeaderField[] hfa_copy;
162 	Frame frame;
163 	hfa_copy = hfa.copy();		
164 	frame.headers = Headers(flags, stream_id, HeadersCategory.HEADERS, PrioritySpec.init, hfa_copy);
165 	frame.headers.pack(bufs, deflater);	
166 	frame.headers.free();
167 }
168 
169 void packPushPromise(Buffers bufs, ref Deflater deflater, int stream_id, FrameFlags flags, int promised_stream_id, in HeaderField[] hfa) {
170 	HeaderField[] hfa_copy;
171 	Frame frame;
172 	hfa_copy = hfa.copy();
173 	
174 	frame.push_promise = PushPromise(flags, stream_id, promised_stream_id, hfa_copy);
175 	frame.push_promise.pack(bufs, deflater);
176 	frame.push_promise.free();
177 }
178 
179 Buffers framePackBuffers() 
180 {
181 	/* 1 for Pad Length */
182 	return new Buffers(4096, 16, FRAME_HDLEN + 1);
183 }
184 
185 Buffers largeBuffers(size_t chunk_size) 
186 {
187 	/* 1 for Pad Length */
188 	return new Buffers(chunk_size, 16, FRAME_HDLEN + 1);
189 }
190 
191 private Stream openStreamWithAll(Session session, int stream_id, int weight, bool exclusive, Stream dep_stream)
192 {
193 	PrioritySpec pri_spec;
194 	int dep_stream_id;
195 	
196 	if (dep_stream) {
197 		dep_stream_id = dep_stream.id;
198 	} else {
199 		dep_stream_id = 0;
200 	}
201 	
202 	pri_spec = PrioritySpec(dep_stream_id, weight, exclusive);
203 
204 	return session.openStream(stream_id, StreamFlags.NONE, pri_spec, StreamState.OPENED, null);
205 }
206 
207 Stream openStream(Session session, int stream_id) 
208 {
209 	return openStreamWithAll(session, stream_id, DEFAULT_WEIGHT, false, null);
210 }
211 
212 Stream openStreamWithDep(Session session, int stream_id, Stream dep_stream)
213 {
214 	return openStreamWithAll(session, stream_id, DEFAULT_WEIGHT, false, dep_stream);
215 }
216 
217 Stream openStreamWithDepWeight(Session session, int stream_id, int weight, Stream dep_stream)
218 {
219 	return openStreamWithAll(session, stream_id, weight, false, dep_stream);
220 }
221 
222 Stream openStreamWithDepExclusive(Session session, int stream_id, Stream dep_stream) 
223 {
224 	return openStreamWithAll(session, stream_id, DEFAULT_WEIGHT, true, dep_stream);
225 }
226 
227 OutboundItem createDataOutboundItem() {
228 	return Mem.alloc!OutboundItem();
229 }