1 /**
2  * Inflater
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.inflater;
13 
14 import libhttp2.types;
15 import libhttp2.buffers;
16 import libhttp2.huffman;
17 import std.algorithm : min;
18 
19 struct Inflater
20 {
21 	HDTable ctx;
22 	
23 	/// header buffer
24 	Buffers hfbufs;
25 	
26 	/// Stores current state of huffman decoding
27 	Decoder huff_decoder;
28 	
29 	/// Pointer to the entry which is used current header emission, for reference counting purposes.
30 	HDEntry ent_keep;
31 	
32 	/// The number of bytes to read
33 	size_t left;
34 	
35 	/// The index in indexed repr or indexed name
36 	size_t index;
37 	
38 	/// The length of new name encoded in literal.  For huffman encoded string, this is the length after it is decoded.
39 	size_t newnamelen;
40 	
41 	/// The maximum header table size the inflater supports. This is the same value transmitted in SettingsID.HEADER_TABLE_SIZE
42 	size_t settings_hd_table_bufsize_max = HD_DEFAULT_MAX_BUFFER_SIZE;
43 	
44 	/// The number of next shift to decode integer 
45 	size_t shift;
46 	
47 	OpCode opcode = OpCode.NONE;
48 	
49 	InflateState state = InflateState.OPCODE;
50 	
51 	/// true if string is huffman encoded
52 	bool huffman_encoded;
53 	
54 	/// true if deflater requires that current entry is indexed
55 	bool index_required;
56 	
57 	/// true if deflater requires that current entry must not be indexed
58 	bool no_index;
59 	
60 	this(bool initialize) {
61 		ctx = Mem.alloc!HDTable();
62 		scope(failure) Mem.free(ctx);
63 		hfbufs = Mem.alloc!Buffers(MAX_HF_LEN / 8, 8, 1, 0);
64 	}
65 
66 	void free() {
67 		if (ent_keep && ent_keep.refcnt == 0) Mem.free(ent_keep);
68 		ent_keep = null;
69 		Mem.free(ctx);
70 		hfbufs.free();
71 		Mem.free(hfbufs);
72 	}
73 
74 	/**
75 	 * Changes header table size in the |Inflater|.  This may trigger
76 	 * eviction in the dynamic table.
77 	 *
78 	 * The |settings_hd_table_bufsize_max| should be the value transmitted
79 	 * in SettingsID.HEADER_TABLE_SIZE.
80 	 */
81 	void changeTableSize(size_t _settings_hd_table_bufsize_max) 
82 	{
83 		settings_hd_table_bufsize_max = _settings_hd_table_bufsize_max;
84 		ctx.hd_table_bufsize_max = _settings_hd_table_bufsize_max;
85 		ctx.shrink();
86 	}
87 
88 	/**
89 	 * Inflates name/value block stored in |input| with length |input.length|. 
90 	 * This function performs decompression.  For each successful emission of
91 	 * header field, $(D InflateFlag.EMIT) is set in |inflate_flags| and a header field
92 	 * is assigned to |hf_out| and the function returns.  The caller must not free 
93 	 * the members of |hf_out|.
94 	 *
95 	 * The |hf_out| may include pointers to the memory region in the |input|.
96 	 * The caller must retain the |input| while the |hf_out| is used.
97 	 *
98 	 * The application should call this function repeatedly until the
99 	 * `(*inflate_flags) & InflateFlag.FINAL` is true and
100 	 * return value is non-negative. This means the all input values are
101 	 * processed successfully.  Then the application must call
102 	 * `endHeaders()` to prepare for the next header
103 	 * block input.
104 	 *
105 	 * The caller can feed complete compressed header block.  It also can
106 	 * feed it in several chunks.  The caller must set |is_final| to
107 	 * true if the given input is the last block of the compressed
108 	 * header.
109 	 *
110 	 * This function returns the number of bytes processed if it succeeds,
111 	 * or one of the following negative error codes:
112 	 *
113 	 * $(D ErrorCode.HEADER_COMP)
114 	 *     Inflation process has failed.
115 	 * $(D ErrorCode.BUFFER_ERROR)
116 	 *     The heder field name or value is too large.
117 	 *
118 	 * Example follows::
119 	 *
120 	 *     void inflateHeaderBlock(ref Inflater hd_inflater, ubyte[] input, bool final)
121 	 *     {
122 	 *         size_t rv;
123 	 *
124 	 *         for(;;) {
125 	 *             HeaderField nv;
126 	 *             InflateFlag inflate_flags;
127 	 *
128 	 *             rv = hd_inflater.inflate(hf, inflate_flags, input, final);
129 	 *
130 	 *             if(rv < 0) {
131 	 *                 fprintf(stderr, "inflate failed with error code %d", rv);
132 	 *                 return;
133 	 *             }
134 	 *
135 	 *             input = input[rv .. $];
136 	 *
137 	 *             if(inflate_flags & InflateFlag.EMIT) {
138 	 *                 writeln(hf.name, " => ", hf.value);
139 	 *             }
140 	 * 
141 	 *             if(inflate_flags & InflateFlag.FINAL) {
142 	 *                 hd_inflater.endHeaders();
143 	 *                 break;
144 	 *             }
145 	 *             if((inflate_flags & InflateFlag.EMIT) == 0 && input.length == 0) {
146 	 *                break;
147 	 *             }
148 	 *         }
149 	 *     }
150 	 *
151 	 */
152 	int inflate()(ref HeaderField hf_out, auto ref InflateFlag inflate_flags, ubyte[] input, bool is_final)
153 	{
154 		ErrorCode rv;
155 		ubyte* pos = input.ptr;
156 		ubyte* first = input.ptr;
157 		ubyte* last = input.ptr + input.length;
158 		bool rfin; // read finished
159 		if (ctx.bad) return ErrorCode.HEADER_COMP;
160 		scope(failure) ctx.bad = 1;
161 		
162 		LOGF("inflatehd: start state=%s pos=%s last=%s", state, pos, last);
163 		if (state == InflateState.READ_VALUE && pos is last)
164 			assert(false);
165 		if (ent_keep && ent_keep.refcnt == 0) Mem.free(ent_keep);
166 		ent_keep = null;
167 		inflate_flags = InflateFlag.NONE;
168 
169 		for (; pos < last;) {
170 			final switch (state) {
171 				case InflateState.OPCODE:
172 					if ((*pos & 0xe0) == 0x20) {
173 						LOGF("inflatehd: header table size change");
174 						opcode = OpCode.INDEXED;
175 						state = InflateState.READ_TABLE_SIZE;
176 					} else if (*pos & 0x80) {
177 						LOGF("inflatehd: indexed repr");
178 						opcode = OpCode.INDEXED;
179 						state = InflateState.READ_INDEX;
180 					} else {
181 						if (*pos == 0x40 || *pos == 0 || *pos == 0x10) {
182 							LOGF("inflatehd: literal header repr - new name");
183 							opcode = OpCode.NEWNAME;
184 							state = InflateState.NEWNAME_CHECK_NAMELEN;
185 						} else {
186 							LOGF("inflatehd: literal header repr - indexed name");
187 							opcode = OpCode.INDNAME;
188 							state = InflateState.READ_INDEX;
189 						}
190 						index_required = (*pos & 0x40) != 0;
191 						no_index = (*pos & 0xf0) == 0x10;
192 						LOGF("inflatehd: indexing required=%d, no_index=%d", index_required, no_index);
193 
194 						if (opcode == OpCode.NEWNAME)
195 							++pos;
196 					}
197 					left = 0;
198 					shift = 0;
199 					break;
200 				case InflateState.READ_TABLE_SIZE:
201 					rfin = false;
202 					int len = readLength(rfin, pos, last, 5, settings_hd_table_bufsize_max);
203 					if (len < 0) {
204 						rv = cast(ErrorCode) len;
205 						goto fail;
206 					}
207 					pos += len;
208 
209 					if (!rfin)
210 						goto almost_ok;
211 
212 					LOGF("inflatehd: table_size=%d", left);
213 					ctx.hd_table_bufsize_max = left;
214 					ctx.shrink();
215 					state = InflateState.OPCODE;
216 					break;
217 				case InflateState.READ_INDEX: {
218 					size_t prefixlen;
219 					
220 					if (opcode == OpCode.INDEXED)
221 						prefixlen = 7;
222 					else if (index_required)
223 						prefixlen = 6;
224 					else 
225 						prefixlen = 4;
226 
227 					rfin = false;
228 					size_t maxlen = ctx.hd_table.length + static_table.length;
229 					int len = readLength(rfin, pos, last, prefixlen, maxlen);
230 
231 					if (len < 0) { 
232 						rv = cast(ErrorCode) len;
233 						goto fail;
234 					}		
235 					pos += len;
236 					
237 					if (!rfin)
238 						goto almost_ok;
239 										
240 					if (left == 0) {
241 						rv = ErrorCode.HEADER_COMP;
242 						goto fail;
243 					}
244 					
245 					LOGF("inflatehd: index=%d", left);
246 					if (opcode == OpCode.INDEXED) {
247 						index = left;
248 						--index;
249 						
250 						hf_out = commitIndexed();
251 
252 						if (hf_out == HeaderField.init)
253 							goto fail;
254 
255 						state = InflateState.OPCODE;
256 						/* If rv == 1, no header was emitted */
257 						if (rv == 0) {
258 							inflate_flags |= InflateFlag.EMIT;
259 							return cast(int)(pos - first);
260 						}
261 					} else {
262 						index = left;
263 						--index;
264 						
265 						state = InflateState.CHECK_VALUELEN;
266 					}
267 					break;
268 				}
269 				case InflateState.NEWNAME_CHECK_NAMELEN:
270 					setHuffmanEncoded(pos);
271 					state = InflateState.NEWNAME_READ_NAMELEN;
272 					left = 0;
273 					shift = 0;
274 					LOGF("inflatehd: huffman encoded=%d", huffman_encoded != 0);
275 					goto case InflateState.NEWNAME_READ_NAMELEN;
276 				case InflateState.NEWNAME_READ_NAMELEN:
277 					rfin = false;
278 					int len = readLength(rfin, pos, last, 7, MAX_HF_LEN);
279 
280 					if (len < 0) {
281 						rv = cast(ErrorCode) len;
282 						goto fail;
283 					}
284 					pos += len;
285 					if (!rfin) {
286 						LOGF("inflatehd: integer not fully decoded. current=%d", left);						
287 						goto almost_ok;
288 					}
289 					
290 					if (huffman_encoded) {
291 						huff_decoder = Decoder.init;						
292 						state = InflateState.NEWNAME_READ_NAMEHUFF;
293 					} else
294 						state = InflateState.NEWNAME_READ_NAME;
295 					break;
296 				case InflateState.NEWNAME_READ_NAMEHUFF:
297 					int len = readHuffman(hfbufs, pos, last);
298 
299 					if (len < 0) {
300 						rv = cast(ErrorCode) len;
301 						goto fail;
302 					}
303 					pos += len;
304 					
305 					LOGF("inflatehd: %d bytes read in NEWNAME_READ_NAMEHUFF", len);
306 					
307 					if (left) {
308 						LOGF("inflatehd: still %d bytes to go", left);
309 						goto almost_ok;
310 					}
311 					
312 					newnamelen = hfbufs.length;
313 					
314 					state = InflateState.CHECK_VALUELEN;
315 					
316 					break;
317 				case InflateState.NEWNAME_READ_NAME:
318 					int len = read(hfbufs, pos, last);
319 
320 					if (len < 0) {
321 						rv = cast(ErrorCode) len;
322 						goto fail;
323 					}
324 					pos += len;
325 					
326 					LOGF("inflatehd: %d bytes read in NEWNAME_READ_NAME", len);
327 					if (left) {
328 						LOGF("inflatehd: still %d bytes to go", left);
329 						
330 						goto almost_ok;
331 					}
332 					
333 					newnamelen = hfbufs.length;
334 					
335 					state = InflateState.CHECK_VALUELEN;
336 					
337 					break;
338 				case InflateState.CHECK_VALUELEN:
339 					setHuffmanEncoded(pos);
340 					state = InflateState.READ_VALUELEN;
341 					left = 0;
342 					shift = 0;
343 					LOGF("inflatehd: huffman encoded=%d", huffman_encoded != 0);
344 
345 					goto case InflateState.READ_VALUELEN;
346 				case InflateState.READ_VALUELEN:
347 					rfin = false;
348 					int len = readLength(rfin, pos, last, 7, MAX_HF_LEN);
349 					if (len < 0) {
350 						rv = cast(ErrorCode) len;
351 						goto fail;
352 					}		
353 					pos += len;
354 					
355 					if (!rfin)
356 						goto almost_ok;
357 										
358 					LOGF("inflatehd: valuelen=%d", left);
359 					if (left == 0) {
360 						if (opcode == OpCode.NEWNAME)
361 							hf_out = commitNewName();
362 						else
363 							hf_out = commitIndexedName();
364 
365 						if (hf_out == HeaderField.init)
366 							goto fail;
367 
368 						state = InflateState.OPCODE;
369 						inflate_flags |= InflateFlag.EMIT;
370 						return cast(int)(pos - first);
371 					}
372 					
373 					if (huffman_encoded) {
374 						huff_decoder = Decoder.init;
375 						
376 						state = InflateState.READ_VALUEHUFF;
377 					} else {
378 						state = InflateState.READ_VALUE;
379 					}
380 					break;
381 				case InflateState.READ_VALUEHUFF:
382 					int len = readHuffman(hfbufs, pos, last);
383 
384 					if (len < 0) {
385 						rv = cast(ErrorCode) len;
386 						goto fail;
387 					}		
388 					pos += len;
389 
390 					LOGF("inflatehd: %d bytes read in READ_VALUEHUFF", len);
391 					
392 					if (left) {
393 						LOGF("inflatehd: still %d bytes to go", left);						
394 						goto almost_ok;
395 					}
396 					
397 					if (opcode == OpCode.NEWNAME) 
398 						hf_out = commitNewName();
399 					else
400 						hf_out = commitIndexedName();
401 					if (hf_out == HeaderField.init) {
402 						goto fail;
403 					}
404 					state = InflateState.OPCODE;
405 					inflate_flags |= InflateFlag.EMIT;
406 
407 					return cast(int)(pos - first);
408 
409 				case InflateState.READ_VALUE:
410 					int len = read(hfbufs, pos, last);
411 
412 					if (len < 0) {
413 						rv = cast(ErrorCode) len;
414 						LOGF("inflatehd: value read failure %d: %s", rv, toString(cast(ErrorCode)rv));
415 						goto fail;
416 					}
417 					
418 					pos += len;
419 					
420 					LOGF("inflatehd: %d bytes read in READ_VALUE", len);
421 					
422 					if (left) {
423 						LOGF("inflatehd: still %d bytes to go", left);
424 						goto almost_ok;
425 					}
426 					
427 					if (opcode == OpCode.NEWNAME)
428 						hf_out = commitNewName();
429 					else
430 						hf_out = commitIndexedName();
431 
432 					if (hf_out == HeaderField.init)
433 						goto fail;
434 
435 					state = InflateState.OPCODE;
436 					inflate_flags |= InflateFlag.EMIT;
437 					
438 					return cast(int)(pos - first);
439 			}
440 		}
441 		
442 		assert(pos is last);
443 		
444 		LOGF("inflatehd: all input bytes were processed");
445 		
446 		if (is_final) {
447 			LOGF("inflatehd: is_final set");
448 			
449 			if (state != InflateState.OPCODE) {
450 				LOGF("inflatehd: unacceptable state=%d", state);
451 				rv = ErrorCode.HEADER_COMP;
452 				
453 				goto fail;
454 			}
455 			inflate_flags |= InflateFlag.FINAL;
456 		}
457 		return cast(int)(pos - first);
458 		
459 	almost_ok:
460 		if (is_final && state != InflateState.OPCODE) {
461 			LOGF("inflatehd: input ended prematurely");			
462 			rv = ErrorCode.HEADER_COMP;			
463 			goto fail;
464 		}
465 
466 		return cast(int)(pos - first);
467 		
468 	fail:
469 		LOGF("inflatehd: error return %d", rv);		
470 		ctx.bad = 1;
471 		return cast(int)rv;
472 		
473 	}
474 
475 	/**
476 	 * Signals the end of decompression for one header block.
477 	 */
478 	void endHeaders() {
479 		if (ent_keep && ent_keep.refcnt == 0) Mem.free(ent_keep);
480 		ent_keep = null;
481 	}
482 
483 	void setHuffmanEncoded(in ubyte* input) {
484 		huffman_encoded = (*input & (cast(ubyte)(1 << 7))) != 0;
485 	}
486 	
487 	/*
488 	 * Decodes the integer from the range [in, last).  The result is
489 	 * assigned to |left|.  If the |left| is 0, then
490 	 * it performs variable integer decoding from scratch. Otherwise, it
491 	 * uses the |left| as the initial value and continues to
492 	 * decode assuming that [in, last) begins with intermediary sequence.
493 	 *
494 	 * This function returns the number of bytes read if it succeeds, or
495 	 * one of the following negative error codes:
496 	 *
497 	 * ErrorCode.HEADER_COMP
498 	 *   Integer decoding failed
499 	 */
500 	int readLength(ref bool is_final, ubyte* input, ubyte* last, size_t prefix, size_t maxlen) 
501 	{
502 		int rv;
503 		uint output;
504 		
505 		is_final = false;
506 
507 		rv = decodeLength(output, shift, is_final, cast(uint)left, shift, input, last, prefix);
508 		
509 		if (rv == -1) {
510 			LOGF("inflatehd: integer decoding failed");
511 			return cast(int)ErrorCode.HEADER_COMP;
512 		}
513 		
514 		if (output > maxlen) {
515 			LOGF("inflatehd: integer exceeded the maximum value %d", maxlen);
516 			return cast(int)ErrorCode.HEADER_COMP;
517 		}
518 		
519 		left = output;
520 		
521 		LOGF("inflatehd: decoded integer is %u", output);
522 		
523 		return rv;
524 	}
525 	
526 	/*
527 	 * Reads |left| bytes from the range [in, last) and performs
528 	 * huffman decoding against them and pushes the result into the
529 	 * |buffer|.
530 	 *
531 	 * This function returns the number of bytes read if it succeeds, or
532 	 * one of the following negative error codes:
533 	 *
534 	 * ErrorCode.HEADER_COMP
535 	 *   Huffman decoding failed
536 	 * ErrorCode.BUFFER_ERROR
537 	 *     Out of buffer space.
538 	 */
539 	int readHuffman(Buffers bufs, ubyte* input, ubyte* last) {
540 		int readlen;
541 		bool rfin;
542 
543 		if (cast(size_t) (last - input) >= left) {
544 			last = input + left;
545 			rfin = true;
546 		}
547 		readlen = huff_decoder.decode(bufs, input[0 .. last - input], rfin);
548 
549 		if (readlen < 0) {
550 			LOGF("inflatehd: huffman decoding failed");
551 			return readlen;
552 		}
553 		left -= cast(size_t)readlen;
554 		return readlen;
555 	}
556 	
557 	/*
558 	 * Reads |left| bytes from the range [in, last) and copies
559 	 * them into the |buffer|.
560 	 *
561 	 * This function returns the number of bytes read if it succeeds, or
562 	 * one of the following negative error codes:
563 	 *
564 	 * ErrorCode.HEADER_COMP
565 	 *   Header decompression failed
566 	 * ErrorCode.BUFFER_ERROR
567 	 *     Out of buffer space.
568 	 */
569 	int read(Buffers bufs, ubyte* input, ubyte* last) 
570 	{
571 		ErrorCode rv;
572 		size_t len = min(cast(size_t)(last - input), left);
573 
574 		rv = bufs.add(cast(string)input[0 .. len]);
575 
576 		if (rv != 0)
577 			return cast(int) rv;
578 
579 		left -= len;
580 		return cast(int) len;
581 	}
582 
583 	HeaderField removeBufs(bool value_only) {
584 		HeaderField hf;
585 		size_t buflen;
586 		ubyte[] buf;
587 		Buffer* pbuf;
588 
589 		if (index_required || hfbufs.head != hfbufs.cur) {
590 			buf = hfbufs.remove();
591 			buflen = buf.length;
592 			
593 			if (value_only)
594 				hf.name = null;
595 			else if (newnamelen > 0) {
596 				hf.name = cast(string)Mem.copy(buf[0 .. newnamelen]);
597 			}
598 			if (buflen - hf.name.length > 0) {
599 				hf.value = cast(string)Mem.copy((buf.ptr + hf.name.length)[0 .. buflen - hf.name.length]);
600 			}
601 			Mem.free(buf);
602 
603 			return hf;
604 		}
605 		
606 		// If we are not going to store header in header table and name/value are in first chunk, 
607 		// we just refer them from hf, instead of mallocing another memory.
608 		pbuf = &hfbufs.head.buf;
609 		
610 		if (value_only)
611 			hf.name = null;
612 		else
613 			hf.name = cast(string)pbuf.pos[0 .. newnamelen];
614 		
615 		hf.value = cast(string)(pbuf.pos + hf.name.length)[0 .. pbuf.length - hf.name.length];
616 		
617 		// Resetting does not change the content of first buffer
618 		hfbufs.reset();
619 		
620 		return hf;
621 	}
622 
623 package:	
624 	/*
625 	 * Finalize literal header representation - new name- reception. If
626 	 * header is emitted, it is returned
627 	 */
628 	HeaderField commitNewName() {
629 		HeaderField hf = removeBufs(false);
630 		HeaderField ret;
631 		if (no_index)
632 			hf.flag = HeaderFlag.NO_INDEX;
633 		else
634 			hf.flag = HeaderFlag.NONE;
635 		
636 		if (index_required) {
637 			HDEntry new_ent;
638 			HDFlags ent_flags;
639 			
640 			ent_flags = HDFlags.NAME_ALLOC | HDFlags.NAME_GIFT | HDFlags.VALUE_ALLOC | HDFlags.VALUE_GIFT;
641 			
642 			new_ent = ctx.add(hf, hf.name.hash(), hf.value.hash(), ent_flags);
643 
644 			if (new_ent) {
645 				ret = emitIndexedHeader(new_ent);
646 				
647 				ent_keep = new_ent;
648 			
649 				return ret;
650 			}
651 
652 			Mem.free(hf.name);
653 			Mem.free(hf.value);
654 
655 			return HeaderField.init;
656 		}
657 		
658 		ret = emitLiteralHeader(hf);
659 
660 		return ret;
661 	}
662 	
663 	/// Finalize literal header representation - indexed name reception. If header is emitted, the HeaderField is returned
664 	HeaderField commitIndexedName() {
665 		HeaderField ret;
666 		HDEntry ent_name;
667 
668 		HeaderField hf = removeBufs(true /* value only */);
669 		
670 		if (no_index)
671 			hf.flag = HeaderFlag.NO_INDEX;
672 		else
673 			hf.flag = HeaderFlag.NONE;
674 		
675 		ent_name = ctx.get(index);		
676 		hf.name = ent_name.hf.name;
677 		
678 		if (index_required) {
679 			HDEntry new_ent;
680 			HDFlags ent_flags;
681 			bool static_name;
682 			
683 			ent_flags = HDFlags.VALUE_ALLOC | HDFlags.VALUE_GIFT;
684 			static_name = index < static_table.length;
685 			
686 			if (!static_name) {
687 				ent_flags |= HDFlags.NAME_ALLOC;
688 				/* For entry in static table, we must not touch ref, because it is shared by threads */
689 				++ent_name.refcnt;
690 			}
691 			new_ent = ctx.add(hf, ent_name.name_hash, hf.value.hash(), ent_flags);
692 
693 			if (!static_name && --ent_name.refcnt == 0) {
694 				Mem.free(ent_name);
695 			}
696 
697 			if (new_ent) {
698 				ret = emitIndexedHeader(new_ent);
699 				ent_keep = new_ent;
700 				return ret;
701 			}
702 
703 			Mem.free(hf.value);
704 
705 			return ret;
706 		}
707 		
708 		ret = emitLiteralHeader(hf);
709 
710 		return ret;
711 	}
712 
713 	/*
714 	 * Finalize indexed header representation reception. If header is
715 	 * emitted, returns it
716 	 */
717 	HeaderField commitIndexed() {
718 		HDEntry ent = ctx.get(index);		
719 		return emitIndexedHeader(ent);
720 	}
721 
722 	// for debugging
723 	HeaderField emitIndexedHeader(ref HDEntry ent) {
724 		LOGF("inflatehd: indexed header emission: %s: %s", ent?ent.hf.name:"null", ent?ent.hf.value:"null");
725 		
726 		return ent?ent.hf : HeaderField.init;
727 	}
728 	
729 	HeaderField emitLiteralHeader(ref HeaderField hf) {
730 		LOGF("inflatehd: literal header emission: %s: %s", hf.name, hf.value);
731 		
732 		return hf;
733 	}
734 
735 
736 }
737 
738