1 /** 2 * Helpers 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.helpers; 13 14 import libhttp2.constants; 15 import std.bitmanip : bigEndianToNative, nativeToBigEndian; 16 import libhttp2.types; 17 import core.stdc..string : memcpy; 18 import std..string : toLowerInPlace; 19 import core.exception : onRangeError; 20 import std.algorithm : max, min; 21 22 void write(T)(ubyte* buf, T n) { 23 auto x = nativeToBigEndian(n); 24 memcpy(buf, x.ptr, T.sizeof); 25 } 26 27 void write(T)(ubyte[] buf, T n) { 28 if (buf.length < T.sizeof) onRangeError(); 29 auto x = nativeToBigEndian(n); 30 memcpy(buf.ptr, x.ptr, T.sizeof); 31 } 32 33 T read(T = uint)(in ubyte* buf) { 34 return bigEndianToNative!T(buf[0 .. T.sizeof]); 35 } 36 37 T read(T = uint)(in ubyte[] buf) { 38 if (buf.length < T.sizeof) onRangeError(); 39 return bigEndianToNative!T(cast(ubyte[T.sizeof])buf[0 .. T.sizeof]); 40 } 41 42 HeaderField[] copy()(auto const ref HeaderField[] hfa) { 43 if (hfa.length == 0) 44 return null; 45 46 HeaderField[] ret = Mem.alloc!(HeaderField[])(hfa.length); 47 48 foreach (size_t i, const ref HeaderField hf; hfa) { 49 ret[i].flag = hf.flag; 50 char[] copy; 51 if (hf.name.length > 0) 52 copy = copyToLower(hf.name); 53 54 ret[i].name = cast(string) copy; 55 if (hf.value.length > 0) 56 ret[i].value = Mem.copy(hf.value); 57 else ret[i].value = null; 58 } 59 return ret; 60 } 61 62 /* 63 * Makes copy of |iv| and return the copy. This function returns the 64 * copy if it succeeds, or null. 65 */ 66 Setting[] copy(in Setting[] iva) { 67 if (iva.length == 0) 68 return null; 69 return cast(Setting[]) Mem.copy(iva); 70 } 71 72 73 bool equals(in HeaderField[] hfa, in HeaderField[] other) 74 { 75 import libhttp2.tests : sort; 76 auto hfa2 = hfa.copy(); 77 auto other2 = other.copy(); 78 scope(exit) { 79 foreach(i, ref hf; hfa2) { 80 hf.free(); 81 } 82 foreach(i, ref hf; other2) { 83 hf.free(); 84 } 85 Mem.free(hfa2); 86 Mem.free(other2); 87 } 88 sort(hfa2); 89 sort(other2); 90 foreach(i, hf; hfa2) 91 if (other2[i] != hf) 92 return false; 93 return true; 94 } 95 96 void free(ref HeaderField[] hfa) 97 { 98 foreach(ref hf; hfa) 99 { 100 hf.free(); 101 } 102 Mem.free(hfa); 103 hfa = null; 104 } 105 106 107 /* 108 * This function was generated by genlibtokenlookup.py. Inspired by 109 * h2o header lookup. https://github.com/h2o/h2o 110 */ 111 Token parseToken(in string name) { 112 with(Token) switch (name.length) { 113 case 2: 114 switch (name[1]) { 115 case 'e': 116 if (name[0] == 't') { 117 return TE; 118 } 119 break; 120 default: break; 121 } 122 break; 123 case 4: 124 switch (name[3]) { 125 case 't': 126 if (name.ptr[0 .. 3] == "hos") { 127 return HOST; 128 } 129 break; 130 default: break; 131 } 132 break; 133 case 5: 134 switch (name[4]) { 135 case 'h': 136 if (name.ptr[0 .. 4] == ":pat") { 137 return _PATH; 138 } 139 break; 140 default: break; 141 } 142 break; 143 case 7: 144 switch (name[6]) { 145 case 'd': 146 if (name.ptr[0 .. 6] == ":metho") { 147 return _METHOD; 148 } 149 break; 150 case 'e': 151 if (name.ptr[0 .. 6] == ":schem") { 152 return _SCHEME; 153 } 154 if (name.ptr[0 .. 6] == "upgrad") { 155 return UPGRADE; 156 } 157 break; 158 case 's': 159 if (name.ptr[0 .. 6] == ":statu") { 160 return _STATUS; 161 } 162 break; 163 default: break; 164 } 165 break; 166 case 10: 167 switch (name[9]) { 168 case 'e': 169 if (name.ptr[0 .. 9] == "keep-aliv") { 170 return KEEP_ALIVE; 171 } 172 break; 173 case 'n': 174 if (name.ptr[0 .. 9] == "connectio") { 175 return CONNECTION; 176 } 177 break; 178 case 'y': 179 if (name.ptr[0 .. 9] == ":authorit") { 180 return _AUTHORITY; 181 } 182 break; 183 default: break; 184 } 185 break; 186 case 14: 187 switch (name[13]) { 188 case 'h': 189 if (name.ptr[0 .. 13] == "content-lengt") { 190 return CONTENT_LENGTH; 191 } 192 break; 193 default: break; 194 } 195 break; 196 case 16: 197 switch (name[15]) { 198 case 'n': 199 if (name.ptr[0 .. 15] == "proxy-connectio") { 200 return PROXY_CONNECTION; 201 } 202 break; 203 default: break; 204 } 205 break; 206 case 17: 207 switch (name[16]) { 208 case 'g': 209 if (name.ptr[0 .. 16] == "transfer-encodin") { 210 return TRANSFER_ENCODING; 211 } 212 break; 213 default: break; 214 } 215 break; 216 default: break; 217 } 218 return Token.ERROR; 219 } 220 221 222 /* 223 * local_window_size 224 * ^ * 225 * | * recv_window_size 226 * | * * ^ 227 * | * * | 228 * 0+++++++++ 229 * | * * \ 230 * | * * | This rage is hidden in flow control. But it must be 231 * v * * / kept in order to restore it when window size is enlarged. 232 * recv_reduction 233 * (+ for negative direction) 234 * 235 * recv_window_size could be negative if we decrease 236 * local_window_size more than recv_window_size: 237 * 238 * local_window_size 239 * ^ * 240 * | * 241 * | * 242 * 0++++++++ 243 * | * ^ recv_window_size (negative) 244 * | * | 245 * v * * 246 * recv_reduction 247 */ 248 ErrorCode adjustLocalWindowSize(ref int local_window_size_ptr, ref int recv_window_size_ptr, ref int recv_reduction_ptr, ref int delta_ptr) 249 { 250 if (delta_ptr > 0) { 251 int recv_reduction_delta; 252 int delta; 253 int new_recv_window_size = max(0, recv_window_size_ptr) - delta_ptr; 254 255 if (new_recv_window_size >= 0) 256 { 257 recv_window_size_ptr = new_recv_window_size; 258 return ErrorCode.OK; 259 } 260 261 delta = -new_recv_window_size; 262 263 /* The delta size is strictly more than received bytes. Increase 264 local_window_size by that difference |delta|. */ 265 if (local_window_size_ptr > MAX_WINDOW_SIZE - delta) 266 { 267 return ErrorCode.FLOW_CONTROL; 268 } 269 local_window_size_ptr += delta; 270 271 /* If there is recv_reduction due to earlier window_size 272 reduction, we have to adjust it too. */ 273 recv_reduction_delta = min(recv_reduction_ptr, delta); 274 275 recv_reduction_ptr -= recv_reduction_delta; 276 277 if (recv_window_size_ptr < 0) { 278 recv_window_size_ptr += recv_reduction_delta; 279 } else { 280 /* If recv_window_size_ptr > 0, then those bytes are going to 281 be returned to the remote peer (by WINDOW_UPDATE with the 282 adjusted delta_ptr), so it is effectively 0 now. We set to 283 recv_reduction_delta, because caller does not take into 284 account it in delta_ptr. */ 285 recv_window_size_ptr = recv_reduction_delta; 286 } 287 288 /* recv_reduction_delta must be paied from delta_ptr, since it 289 was added in window size reduction (see below). */ 290 delta_ptr -= recv_reduction_delta; 291 292 return ErrorCode.OK; 293 } 294 295 if (local_window_size_ptr + delta_ptr < 0 || 296 recv_window_size_ptr < int.min - delta_ptr || 297 recv_reduction_ptr > int.max + delta_ptr) 298 { 299 return ErrorCode.FLOW_CONTROL; 300 } 301 /* Decreasing local window size. Note that we achieve this without 302 noticing to the remote peer. To do this, we cut 303 recv_window_size by -delta. This means that we don't send 304 WINDOW_UPDATE for -delta bytes. */ 305 306 local_window_size_ptr += delta_ptr; 307 recv_window_size_ptr += delta_ptr; 308 recv_reduction_ptr -= delta_ptr; 309 delta_ptr = 0; 310 311 return ErrorCode.OK; 312 } 313 314 bool shouldSendWindowUpdate(int local_window_size, int recv_window_size) { 315 return recv_window_size >= local_window_size / 5; 316 } 317 318 char[] copyToLower(string str) { 319 char[] str_copy = cast(char[])Mem.copy(str); 320 321 for (size_t i = 0; i < str_copy.length; i++) { 322 size_t idx = cast(size_t) cast(ubyte) str_copy[i]; 323 str_copy[i] = DOWNCASE_TBL[idx]; 324 } 325 326 return str_copy; 327 } 328 329 /* Generated by gendowncasetbl.py */ 330 immutable char[] DOWNCASE_TBL = [ 331 0 /* NUL */, 1 /* SOH */, 2 /* STX */, 3 /* ETX */, 332 4 /* EOT */, 5 /* ENQ */, 6 /* ACK */, 7 /* BEL */, 333 8 /* BS */, 9 /* HT */, 10 /* LF */, 11 /* VT */, 334 12 /* FF */, 13 /* CR */, 14 /* SO */, 15 /* SI */, 335 16 /* DLE */, 17 /* DC1 */, 18 /* DC2 */, 19 /* DC3 */, 336 20 /* DC4 */, 21 /* NAK */, 22 /* SYN */, 23 /* ETB */, 337 24 /* CAN */, 25 /* EM */, 26 /* SUB */, 27 /* ESC */, 338 28 /* FS */, 29 /* GS */, 30 /* RS */, 31 /* US */, 339 32 /* SPC */, 33 /* ! */, 34 /* " */, 35 /* # */, 340 36 /* $ */, 37 /* % */, 38 /* & */, 39 /* ' */, 341 40 /* ( */, 41 /* ) */, 42 /* * */, 43 /* + */, 342 44 /* , */, 45 /* - */, 46 /* . */, 47 /* / */, 343 48 /* 0 */, 49 /* 1 */, 50 /* 2 */, 51 /* 3 */, 344 52 /* 4 */, 53 /* 5 */, 54 /* 6 */, 55 /* 7 */, 345 56 /* 8 */, 57 /* 9 */, 58 /* : */, 59 /* ; */, 346 60 /* < */, 61 /* = */, 62 /* > */, 63 /* ? */, 347 64 /* @ */, 97 /* A */, 98 /* B */, 99 /* C */, 348 100 /* D */, 101 /* E */, 102 /* F */, 103 /* G */, 349 104 /* H */, 105 /* I */, 106 /* J */, 107 /* K */, 350 108 /* L */, 109 /* M */, 110 /* N */, 111 /* O */, 351 112 /* P */, 113 /* Q */, 114 /* R */, 115 /* S */, 352 116 /* T */, 117 /* U */, 118 /* V */, 119 /* W */, 353 120 /* X */, 121 /* Y */, 122 /* Z */, 91 /* [ */, 354 92 /* \ */, 93 /* ] */, 94 /* ^ */, 95 /* _ */, 355 96 /* ` */, 97 /* a */, 98 /* b */, 99 /* c */, 356 100 /* d */, 101 /* e */, 102 /* f */, 103 /* g */, 357 104 /* h */, 105 /* i */, 106 /* j */, 107 /* k */, 358 108 /* l */, 109 /* m */, 110 /* n */, 111 /* o */, 359 112 /* p */, 113 /* q */, 114 /* r */, 115 /* s */, 360 116 /* t */, 117 /* u */, 118 /* v */, 119 /* w */, 361 120 /* x */, 121 /* y */, 122 /* z */, 123 /* { */, 362 124 /* | */, 125 /* } */, 126 /* ~ */, 127 /* DEL */, 363 128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */, 364 132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */, 365 136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */, 366 140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */, 367 144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */, 368 148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */, 369 152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */, 370 156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */, 371 160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */, 372 164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */, 373 168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */, 374 172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */, 375 176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */, 376 180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */, 377 184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */, 378 188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */, 379 192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */, 380 196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */, 381 200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */, 382 204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */, 383 208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */, 384 212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */, 385 216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */, 386 220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */, 387 224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */, 388 228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */, 389 232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */, 390 236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */, 391 240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */, 392 244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */, 393 248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */, 394 252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */ 395 ]; 396 397 398 char downcase(char c) { 399 return cast(char)('A' <= c && c <= 'Z' ? (c - 'A' + 'a') : c); 400 } 401 402 bool memieq(const void *a, const void *b, size_t n) { 403 size_t i; 404 const ubyte* aa = cast(const ubyte*) a; 405 const ubyte* bb = cast(const ubyte*) b; 406 407 for (i = 0; i < n; ++i) { 408 if (downcase(aa[i]) != downcase(bb[i])) { 409 return false; 410 } 411 } 412 return true; 413 }