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 }