-2

Message variable length indicator (VLI) 2 bytes precede every message sent to/from BizSwitch. The 2 bytes are referred to as a variable length indicator. Bytes 1-2 indicate the number of bytes in the message (excluding the first 2 bytes). The 2 bytes represent a 16bit unsigned integer in network byte order. Note that if a compressed message is being sent, the message will have to first be compressed, in order to determine its length, before being sent. As an example, suppose that you were to look at just the text (excluding the 2 byte header) making up an XML message from a particular example and that you then counted the characters and they happened to add up to 299 characters. If you took a scientific calculator (the one in Windows for example) and you typed in 299 with "Dec" (for decimal) selected, then you select "Hex", the value that would be shown is "12B", which means the same as 01 2B, which is exactly what one would expect to see for the first two bytes of the message if the message had been dumped to a file and then opened with a hex editor. Example java code for calculating a VLI:

public byte[] wrap(byte[] msg) throws Exception {
    int len = msg.length;
    if (len > 65535) {
        throw new IllegalArgumentException("Exceeds 65535 bytes.");
    }
    byte firstByte = (byte)(len >>> 8);
    byte secondByte = (byte)len;
    ByteArrayOutputStream baos = new ByteArrayOutputStream(len + 2);
    baos.write(firstByte);
    baos.write(secondByte);
    baos.write(msg);
    return baos.toByteArray();
}

public byte[] unWrap(InputStream inputStream) throws Exception {
    int firstByte = inputStream.read();
    if (firstByte == -1) {
        throw new IOException("End of Stream while trying to read vli byte 1");
    }
    int firstByteValue = firstByte << 8;
    int secondByteValue = inputStream.read();
    if (secondByteValue == -1) {
        throw new IOException("End of Stream reading vli byte 2." );
    }
    int len = firstByteValue + secondByteValue;
    byte[] message = new byte[len];
    int requestLen;
    int readLen;
    int currentIndex = 0;
    while(true) {
        requestLen = len - currentIndex;
        readLen = inputStream.read(message, currentIndex, requestLen);
        if (readLen == requestLen) {
            break; // Message is complete.
        }
        // Either data was not yet available, or End of Stream.
        currentIndex += readLen;
        int nextByte = inputStream.read();
        if (nextByte == -1) {
            throw new IOException("End of Stream at " + currentIndex 
    );
        }
        message[currentIndex++] = (byte)nextByte;
    }
return message;
}

and here is my python converted code

import io

def wrap(msg):
    msg_len = len(msg)
    if msg_len > 65535:
        return "Message exceeds 65535 bytes."

    first_byte = bytes(msg_len >> 8)

    second_byte = bytes(msg_len)
    # create an empty bytearray
    data_frame = bytearray()
    data_frame.extend(first_byte)
    data_frame.extend(second_byte)
    data_frame.extend(msg)
    return data_frame



def un_wrap(data_frame):
    data_frame = io.BytesIO(data_frame)

    first_byte = data_frame.read()
    try:
        first_byte == -1
    except:
        raise "End of Stream while trying to read vli byte 1"

    first_byte_value = first_byte << 8

    second_byte_value = data_frame.read(1)

    try:
        second_byte_value == -1
    except:
        raise "End of Stream reading vli byte 2."

    byt_len = first_byte_value + second_byte_value

    message = bytes(byt_len)

   request_len = 0
   read_len = 0
   current_index = 0
   while True:
        request_len = byt_len - current_index
        read_len = data_frame.read(message, current_index, request_len)
        if read_len == request_len:
            return "Message is complete."
        # Either data was not yet available, or End of Stream.
        current_index += read_len
        next_byte = data_frame
        if next_byte == -1:
            return  "End of Stream at " + current_index
        message.extend(bytes(next_byte))
    return message

This is the error am getting

    first_byte_value = first_byte << 8
    TypeError: unsupported operand type(s) for <<: 'bytes' and 'int'

This is the is what am sending as the message but I require two bytes to precede the message

<ipayMsg client="PESATRANS" term="00001" seqNum="0" time="2019-10-09 10:13:20 +0300">
   <elecMsg ver="2.44">
    <vendReq>
      <ref>749761497475</ref>
      <amt cur="KES">500</amt>
      <numTokens>1</numTokens>
      <meter>01450344831</meter>
      <payType>cash</payType>
    </vendReq>
 </elecMsg>
</ipayMsg>
James Njuguna
  • 604
  • 2
  • 12
  • 24

2 Answers2

0

in this line, the code wants to do a left shift on a bytes object. You need to convert it to int first.

int_value = int.frombytes(bytes_value, byteorder="big")

Documentation: https://docs.python.org/3/library/stdtypes.html?highlight=from_bytes#int.from_bytes

bb1950328
  • 1,403
  • 11
  • 18
0

I have figured out the answer. The main issue that I had is to assume that python byte conversion is the same as java. This is where my main challenge was.

public byte[] wrap(byte[] msg) throws Exception {
    int len = msg.length;
    if (len > 65535) {
        throw new IllegalArgumentException("Exceeds 65535 bytes.");
    }
    byte firstByte = (byte)(len >>> 8);
    byte secondByte = (byte)len;
    ByteArrayOutputStream baos = new ByteArrayOutputStream(len + 2);
    baos.write(firstByte);
    baos.write(secondByte);
    baos.write(msg);
    return baos.toByteArray();
}

For this java method I was able to come up this one for python

def wrap(msg):
    msg_len = len(msg)
    if msg_len > 65535:
        return "Message exceeds 65535 bytes."
    my_list = []
    first_byte = msg_len >> 8
    second_byte = msg_len
    my_list.append(first_byte)
    my_list.append(second_byte)

    # create an empty bytearray
    data_frame = bytearray([x % 256 for x in my_list])
    data_frame.extend(msg)
    return data_frame

I was able to achieve this by first creating a list of my elements then I converted from 2s compliment to an unsigned integer and added them into the bytearray.

data_frame = bytearray([x % 256 for x in my_list])

Turns out I do not need the second method. Thanks

James Njuguna
  • 604
  • 2
  • 12
  • 24