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