1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 module thrift.protocol.binary; 20 21 import std.array : uninitializedArray; 22 import std.typetuple : allSatisfy, TypeTuple; 23 import thrift.protocol.base; 24 import thrift.transport.base; 25 import thrift.internal.endian; 26 27 /** 28 * TProtocol implementation of the Binary Thrift protocol. 29 */ 30 final class TBinaryProtocol(Transport = TTransport) if ( 31 isTTransport!Transport 32 ) : TProtocol { 33 34 /** 35 * Constructs a new instance. 36 * 37 * Params: 38 * trans = The transport to use. 39 * containerSizeLimit = If positive, the container size is limited to the 40 * given number of items. 41 * stringSizeLimit = If positive, the string length is limited to the 42 * given number of bytes. 43 * strictRead = If false, old peers which do not include the protocol 44 * version are tolerated. 45 * strictWrite = Whether to include the protocol version in the header. 46 */ 47 this(Transport trans, int containerSizeLimit = 0, int stringSizeLimit = 0, 48 bool strictRead = false, bool strictWrite = true 49 ) { 50 trans_ = trans; 51 this.containerSizeLimit = containerSizeLimit; 52 this.stringSizeLimit = stringSizeLimit; 53 this.strictRead = strictRead; 54 this.strictWrite = strictWrite; 55 } 56 57 Transport transport() @property { 58 return trans_; 59 } 60 61 void reset() {} 62 63 /** 64 * If false, old peers which do not include the protocol version in the 65 * message header are tolerated. 66 * 67 * Defaults to false. 68 */ 69 bool strictRead; 70 71 /** 72 * Whether to include the protocol version in the message header (older 73 * versions didn't). 74 * 75 * Defaults to true. 76 */ 77 bool strictWrite; 78 79 /** 80 * If positive, limits the number of items of deserialized containers to the 81 * given amount. 82 * 83 * This is useful to avoid allocating excessive amounts of memory when broken 84 * data is received. If the limit is exceeded, a SIZE_LIMIT-type 85 * TProtocolException is thrown. 86 * 87 * Defaults to zero (no limit). 88 */ 89 int containerSizeLimit; 90 91 /** 92 * If positive, limits the length of deserialized strings/binary data to the 93 * given number of bytes. 94 * 95 * This is useful to avoid allocating excessive amounts of memory when broken 96 * data is received. If the limit is exceeded, a SIZE_LIMIT-type 97 * TProtocolException is thrown. 98 * 99 * Defaults to zero (no limit). 100 */ 101 int stringSizeLimit; 102 103 /* 104 * Writing methods. 105 */ 106 107 void writeBool(bool b) { 108 writeByte(b ? 1 : 0); 109 } 110 111 void writeByte(byte b) { 112 trans_.write((cast(ubyte*)&b)[0 .. 1]); 113 } 114 115 void writeI16(short i16) { 116 short net = hostToNet(i16); 117 trans_.write((cast(ubyte*)&net)[0 .. 2]); 118 } 119 120 void writeI32(int i32) { 121 int net = hostToNet(i32); 122 trans_.write((cast(ubyte*)&net)[0 .. 4]); 123 } 124 125 void writeI64(long i64) { 126 long net = hostToNet(i64); 127 trans_.write((cast(ubyte*)&net)[0 .. 8]); 128 } 129 130 void writeDouble(double dub) { 131 static assert(double.sizeof == ulong.sizeof); 132 auto bits = hostToNet(*cast(ulong*)(&dub)); 133 trans_.write((cast(ubyte*)&bits)[0 .. 8]); 134 } 135 136 void writeString(string str) { 137 writeBinary(cast(ubyte[])str); 138 } 139 140 void writeBinary(ubyte[] buf) { 141 assert(buf.length <= int.max); 142 writeI32(cast(int)buf.length); 143 trans_.write(buf); 144 } 145 146 void writeMessageBegin(TMessage message) { 147 if (strictWrite) { 148 int versn = VERSION_1 | message.type; 149 writeI32(versn); 150 writeString(message.name); 151 writeI32(message.seqid); 152 } else { 153 writeString(message.name); 154 writeByte(message.type); 155 writeI32(message.seqid); 156 } 157 } 158 void writeMessageEnd() {} 159 160 void writeStructBegin(TStruct tstruct) {} 161 void writeStructEnd() {} 162 163 void writeFieldBegin(TField field) { 164 writeByte(field.type); 165 writeI16(field.id); 166 } 167 void writeFieldEnd() {} 168 169 void writeFieldStop() { 170 writeByte(TType.STOP); 171 } 172 173 void writeListBegin(TList list) { 174 assert(list.size <= int.max); 175 writeByte(list.elemType); 176 writeI32(cast(int)list.size); 177 } 178 void writeListEnd() {} 179 180 void writeMapBegin(TMap map) { 181 assert(map.size <= int.max); 182 writeByte(map.keyType); 183 writeByte(map.valueType); 184 writeI32(cast(int)map.size); 185 } 186 void writeMapEnd() {} 187 188 void writeSetBegin(TSet set) { 189 assert(set.size <= int.max); 190 writeByte(set.elemType); 191 writeI32(cast(int)set.size); 192 } 193 void writeSetEnd() {} 194 195 196 /* 197 * Reading methods. 198 */ 199 200 bool readBool() { 201 return readByte() != 0; 202 } 203 204 byte readByte() { 205 ubyte[1] b = void; 206 trans_.readAll(b); 207 return cast(byte)b[0]; 208 } 209 210 short readI16() { 211 IntBuf!short b = void; 212 trans_.readAll(b.bytes); 213 return netToHost(b.value); 214 } 215 216 int readI32() { 217 IntBuf!int b = void; 218 trans_.readAll(b.bytes); 219 return netToHost(b.value); 220 } 221 222 long readI64() { 223 IntBuf!long b = void; 224 trans_.readAll(b.bytes); 225 return netToHost(b.value); 226 } 227 228 double readDouble() { 229 IntBuf!long b = void; 230 trans_.readAll(b.bytes); 231 b.value = netToHost(b.value); 232 return *cast(double*)(&b.value); 233 } 234 235 string readString() { 236 return cast(string)readBinary(); 237 } 238 239 ubyte[] readBinary() { 240 return readBinaryBody(readSize(stringSizeLimit)); 241 } 242 243 TMessage readMessageBegin() { 244 TMessage msg = void; 245 246 int size = readI32(); 247 if (size < 0) { 248 int versn = size & VERSION_MASK; 249 if (versn != VERSION_1) { 250 throw new TProtocolException("Bad protocol version.", 251 TProtocolException.Type.BAD_VERSION); 252 } 253 254 msg.type = cast(TMessageType)(size & MESSAGE_TYPE_MASK); 255 msg.name = readString(); 256 msg.seqid = readI32(); 257 } else { 258 if (strictRead) { 259 throw new TProtocolException( 260 "Protocol version missing, old client?", 261 TProtocolException.Type.BAD_VERSION); 262 } else { 263 if (size < 0) { 264 throw new TProtocolException(TProtocolException.Type.NEGATIVE_SIZE); 265 } 266 msg.name = cast(string)readBinaryBody(size); 267 msg.type = cast(TMessageType)(readByte()); 268 msg.seqid = readI32(); 269 } 270 } 271 272 return msg; 273 } 274 void readMessageEnd() {} 275 276 TStruct readStructBegin() { 277 return TStruct(); 278 } 279 void readStructEnd() {} 280 281 TField readFieldBegin() { 282 TField f = void; 283 f.name = null; 284 f.type = cast(TType)readByte(); 285 if (f.type == TType.STOP) return f; 286 f.id = readI16(); 287 return f; 288 } 289 void readFieldEnd() {} 290 291 TList readListBegin() { 292 return TList(cast(TType)readByte(), readSize(containerSizeLimit)); 293 } 294 void readListEnd() {} 295 296 TMap readMapBegin() { 297 return TMap(cast(TType)readByte(), cast(TType)readByte(), 298 readSize(containerSizeLimit)); 299 } 300 void readMapEnd() {} 301 302 TSet readSetBegin() { 303 return TSet(cast(TType)readByte(), readSize(containerSizeLimit)); 304 } 305 void readSetEnd() {} 306 307 private: 308 ubyte[] readBinaryBody(int size) { 309 if (size == 0) { 310 return null; 311 } 312 313 auto buf = uninitializedArray!(ubyte[])(size); 314 trans_.readAll(buf); 315 return buf; 316 } 317 318 int readSize(int limit) { 319 auto size = readI32(); 320 if (size < 0) { 321 throw new TProtocolException(TProtocolException.Type.NEGATIVE_SIZE); 322 } else if (limit > 0 && size > limit) { 323 throw new TProtocolException(TProtocolException.Type.SIZE_LIMIT); 324 } 325 return size; 326 } 327 328 enum MESSAGE_TYPE_MASK = 0x000000ff; 329 enum VERSION_MASK = 0xffff0000; 330 enum VERSION_1 = 0x80010000; 331 332 Transport trans_; 333 } 334 335 /** 336 * TBinaryProtocol construction helper to avoid having to explicitly specify 337 * the transport type, i.e. to allow the constructor being called using IFTI 338 * (see $(LINK2 http://d.puremagic.com/issues/show_bug.cgi?id=6082, D Bugzilla 339 * enhancement requet 6082)). 340 */ 341 TBinaryProtocol!Transport tBinaryProtocol(Transport)(Transport trans, 342 int containerSizeLimit = 0, int stringSizeLimit = 0, 343 bool strictRead = false, bool strictWrite = true 344 ) if (isTTransport!Transport) { 345 return new TBinaryProtocol!Transport(trans, containerSizeLimit, 346 stringSizeLimit, strictRead, strictWrite); 347 } 348 349 unittest { 350 import std.exception; 351 import thrift.transport.memory; 352 353 // Check the message header format. 354 auto buf = new TMemoryBuffer; 355 auto binary = tBinaryProtocol(buf); 356 binary.writeMessageBegin(TMessage("foo", TMessageType.CALL, 0)); 357 358 auto header = new ubyte[15]; 359 buf.readAll(header); 360 enforce(header == [ 361 128, 1, 0, 1, // Version 1, TMessageType.CALL 362 0, 0, 0, 3, // Method name length 363 102, 111, 111, // Method name ("foo") 364 0, 0, 0, 0, // Sequence id 365 ]); 366 } 367 368 unittest { 369 import thrift.internal.test.protocol; 370 testContainerSizeLimit!(TBinaryProtocol!())(); 371 testStringSizeLimit!(TBinaryProtocol!())(); 372 } 373 374 /** 375 * TProtocolFactory creating a TBinaryProtocol instance for passed in 376 * transports. 377 * 378 * The optional Transports template tuple parameter can be used to specify 379 * one or more TTransport implementations to specifically instantiate 380 * TBinaryProtocol for. If the actual transport types encountered at 381 * runtime match one of the transports in the list, a specialized protocol 382 * instance is created. Otherwise, a generic TTransport version is used. 383 */ 384 class TBinaryProtocolFactory(Transports...) if ( 385 allSatisfy!(isTTransport, Transports) 386 ) : TProtocolFactory { 387 /// 388 this (int containerSizeLimit = 0, int stringSizeLimit = 0, 389 bool strictRead = false, bool strictWrite = true 390 ) { 391 strictRead_ = strictRead; 392 strictWrite_ = strictWrite; 393 containerSizeLimit_ = containerSizeLimit; 394 stringSizeLimit_ = stringSizeLimit; 395 } 396 397 TProtocol getProtocol(TTransport trans) const { 398 foreach (Transport; TypeTuple!(Transports, TTransport)) { 399 auto concreteTrans = cast(Transport)trans; 400 if (concreteTrans) { 401 return new TBinaryProtocol!Transport(concreteTrans, 402 containerSizeLimit_, stringSizeLimit_, strictRead_, strictWrite_); 403 } 404 } 405 throw new TProtocolException( 406 "Passed null transport to TBinaryProtocolFactoy."); 407 } 408 409 protected: 410 bool strictRead_; 411 bool strictWrite_; 412 int containerSizeLimit_; 413 int stringSizeLimit_; 414 }