/** * Receive a message from the connection. If you set requiredMsgType to the required * message type id instead of zero, it will check the incoming message type and * raise a PyroException if they don't match. */ public static Message getMessage(Stream connection, int requiredMsgType) { byte[] headerdata = IOUtil.recv(connection, HEADER_SIZE); MessageHeader header = parseMessageHeader(headerdata); if (requiredMsgType != 0 && header.type != requiredMsgType) { throw new PyroException("invalid msg type received: " + header.type); } byte[] data = IOUtil.recv(connection, header.datasize); if (Config.MSG_TRACE_DIR != null) { TraceMessageRecv(header.sequence, headerdata, data); } if (((header.flags & FLAGS_HMAC) != 0) && (Config.HMAC_KEY != null)) { if (header.hmac != makeHMAC(data)) { throw new PyroException("message hmac mismatch"); } } else if (((header.flags & FLAGS_HMAC) != 0) != (Config.HMAC_KEY != null)) { throw new PyroException("hmac key config not symmetric"); } Message msg = new Message(); msg.type = header.type; msg.flags = header.flags; msg.sequence = header.sequence; msg.data = data; return(msg); }
// Note: this 'chunked' way of sending is not used because it triggers Nagle's algorithm // on some systems (linux). This causes massive delays, unless you change the socket option // TCP_NODELAY to disable the algorithm. What also works, is sending all the message bytes // in one go: connection.send(message.to_bytes()) // public void send(Stream connection) // { // // send the message as bytes over the connection // IOUtil.send(connection, get_header_bytes()); // if(annotations_size>0) // IOUtil.send(connection, get_annotations_bytes()); // IOUtil.send(connection, data); // } /// <summary> /// Receives a pyro message from a given connection. /// Accepts the given message types (None=any, or pass a sequence). /// Also reads annotation chunks and the actual payload data. /// Validates a HMAC chunk if present. /// </summary> public static Message recv(Stream connection, ushort[] requiredMsgTypes, byte[] hmac) { byte[] header_data = IOUtil.recv(connection, HEADER_SIZE); var msg = from_header(header_data); if (requiredMsgTypes != null && !requiredMsgTypes.Contains(msg.type)) { throw new PyroException(string.Format("invalid msg type {0} received", msg.type)); } byte[] annotations_data = null; msg.annotations = new Dictionary <string, byte[]>(); if (msg.annotations_size > 0) { // read annotation chunks annotations_data = IOUtil.recv(connection, msg.annotations_size); int i = 0; while (i < msg.annotations_size) { string anno = Encoding.ASCII.GetString(annotations_data, i, 4); int length = (annotations_data[i + 4] << 8) | annotations_data[i + 5]; byte[] annotations_bytes = new byte[length]; Array.Copy(annotations_data, i + 6, annotations_bytes, 0, length); msg.annotations[anno] = annotations_bytes; i += 6 + length; } } // read data msg.data = IOUtil.recv(connection, msg.data_size); if (Config.MSG_TRACE_DIR != null) { TraceMessageRecv(msg.seq, header_data, annotations_data, msg.data); } if (msg.annotations.ContainsKey("HMAC") && hmac != null) { if (!msg.annotations["HMAC"].SequenceEqual <byte>(msg.hmac(hmac))) { throw new PyroException("message hmac mismatch"); } } else if (msg.annotations.ContainsKey("HMAC") != (hmac != null)) { // Message contains hmac and local HMAC_KEY not set, or vice versa. This is not allowed. throw new PyroException("hmac key config not symmetric"); } return(msg); }
// Note: this 'chunked' way of sending is not used because it triggers Nagle's algorithm // on some systems (linux). This causes massive delays, unless you change the socket option // TCP_NODELAY to disable the algorithm. What also works, is sending all the message bytes // in one go: connection.send(message.to_bytes()) // public void send(Stream connection) // { // // send the message as bytes over the connection // IOUtil.send(connection, get_header_bytes()); // if(annotations_size>0) // IOUtil.send(connection, get_annotations_bytes()); // IOUtil.send(connection, data); // } /// <summary> /// Receives a pyro message from a given connection. /// Accepts the given message types (None=any, or pass a sequence). /// Also reads annotation chunks and the actual payload data. /// </summary> // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Global public static Message recv(Stream connection, byte[] requiredMsgTypes) { var header_data = IOUtil.recv(connection, HEADER_SIZE); var msg = from_header(header_data); if (requiredMsgTypes != null && !requiredMsgTypes.Contains(msg.type)) { throw new PyroException($"invalid msg type {msg.type} received"); } byte[] annotations_data = null; msg.annotations = new Dictionary <string, byte[]>(); if (msg.annotations_size > 0) { // read annotation chunks annotations_data = IOUtil.recv(connection, msg.annotations_size); int i = 0; while (i < msg.annotations_size) { string anno = Encoding.ASCII.GetString(annotations_data, i, 4); int length = (annotations_data[i + 4] << 24) | (annotations_data[i + 5] << 16) | (annotations_data[i + 6] << 8) | annotations_data[i + 7]; var annotations_bytes = new byte[length]; Array.Copy(annotations_data, i + 8, annotations_bytes, 0, length); msg.annotations[anno] = annotations_bytes; i += 8 + length; } } // read data msg.data = IOUtil.recv(connection, msg.data_size); if (Config.MSG_TRACE_DIR != null) { TraceMessageRecv(msg.seq, header_data, annotations_data, msg.data); } return(msg); }