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