1 module requests.rangeadapter; 2 3 4 /************************************************************************\ 5 /* * 6 /* I need this module to unify input content for POST/PUT requests. * 7 /* Input content must be converted to ubyte[] or ubyte[][]. * 8 /* In latter case it will be transferred in transfer-Encoding: chunked. * 9 /* I need to erase type of input range and convert it to bytes, because * 10 /* interceptors have no access to input range type. * 11 /* * 12 ************************************************************************/ 13 14 import std.format; 15 import std.range.primitives; 16 17 private template rank(R) { 18 static if ( isInputRange!R ) { 19 enum size_t rank = 1 + rank!(ElementType!R); 20 } else { 21 enum size_t rank = 0; 22 } 23 } 24 25 /* 26 * Convert rank 1 and 2 ranges to rank(2) range. 27 * Rank(1) converted to [[chunk]] 28 * Rank(2) used as is: [[chunk],[chunk],...] 29 */ 30 private struct Adapter(R) { 31 R _r; 32 long _length = -1; 33 34 enum _rank = rank!R; 35 static if ( _rank == 1 ){ 36 bool _empty = false; 37 } 38 this(R r) 39 { 40 _r = r; 41 static if ( _rank == 1 && hasLength!R ) { 42 _length = _r.length; 43 } 44 } 45 immutable(ubyte)[] front() 46 { 47 static if ( _rank == 1 ) 48 { 49 return cast(immutable(ubyte)[])_r[0..$]; // return whole array 50 } 51 static if ( _rank == 2 ) 52 { 53 return cast(immutable(ubyte)[])_r.front; // return front chunk 54 } 55 } 56 bool empty() 57 { 58 static if ( _rank == 1 ) 59 { 60 return _empty; 61 } 62 static if ( _rank == 2 ) 63 { 64 return _r.empty; 65 } 66 } 67 void popFront() 68 { 69 static if ( _rank == 1 ) 70 { 71 _empty = true; // no data after pop for rank1 72 } 73 static if ( _rank == 2 ) 74 { 75 _r.popFront; 76 } 77 } 78 } 79 80 private auto ma(R)(R r) { 81 return new Adapter!R(r); 82 } 83 84 /// 85 /// makeAdapter convert input range of ubytes (rank 1 or rank 2), 86 /// So that it can be used in POST requests 87 /// You can use it in Interceptors to modify/set POST data. 88 /// 89 public InputRangeAdapter makeAdapter(R)(R r) { 90 auto adapter = ma(r); 91 InputRangeAdapter result; 92 result._front = &adapter.front; 93 result._empty = &adapter.empty; 94 result._popFront = &adapter.popFront; 95 result._length = adapter._length; 96 return result; 97 } 98 99 /******************************************** 100 * This struct erase Type of the input range 101 * for POST requests, so that I can avoid 102 * templated functions for these requests. 103 * It is important for Interceptors which 104 * have no idea about user input types. 105 * Also it convert any rank 1 or 2 ranges to 106 * rank(2) so that I can use unified code later 107 ********************************************/ 108 struct InputRangeAdapter { 109 private { 110 immutable(ubyte)[] delegate() _front; 111 bool delegate() _empty; 112 void delegate() _popFront; 113 long _length = -1; 114 } 115 immutable(ubyte)[] front() { 116 return _front(); 117 } 118 bool empty() const { 119 if ( _empty is null ) 120 { 121 return true; 122 } 123 return _empty(); 124 } 125 void popFront() { 126 _popFront(); 127 } 128 long length() const { 129 return _length; 130 } 131 } 132 133 unittest { 134 import std.string; 135 import std.algorithm.comparison; 136 import std.stdio; 137 138 auto s0 = "abc"; 139 InputRangeAdapter ira = makeAdapter(s0); 140 assert(ira.equal(["abc"])); 141 142 auto s1 = "abc".representation(); 143 ira = makeAdapter(s1); 144 assert(ira.equal(["abc".representation()])); 145 146 auto s2 = ["abc".representation, "кококо".representation]; 147 ira = makeAdapter(s2); 148 assert(ira.equal(s2)); 149 150 auto f = File("README.md", "r"); 151 auto r = f.byLine(); 152 ira = makeAdapter(r); 153 }