// if message is a request, 'request' is null // if message is a response, 'request' can optionally contain original request public void Add(BplMessage message, BplMessage request) { if (message == null) return; lock (this) { _queue.Enqueue(new OutboxEntry { Message = message, OriginalRequest = request }); } }
/// <summary>Serializes <see cref="BplMessage"/> so that it's ready for network transporting.</summary> public static byte[] Serialize(BplMessage message) { Assumption.NotNull(message); var writer = new BplBinaryWriter(); writer.Serialize(message); var protocol = message.GetProtocol(); var body = writer.ToByteArray(); if (body.Length > protocol.MaxPayload) BplRuntimeException.Throw("Serialized message payload ({0}) is more than maximum allowed ({1})".Substitute(body.Length, protocol.MaxPayload)); var data = new byte[HeaderSize + body.Length]; Buffer.BlockCopy(BitConverter.GetBytes(body.Length), 0, data, 0, 4); Buffer.BlockCopy(_serializeProtocolVersion(protocol.Version), 0, data, 4, 8); Buffer.BlockCopy(BitConverter.GetBytes(CRC32.Compute(data, 0, HeaderSize - 4)), 0, data, 12, 4); Buffer.BlockCopy(body, 0, data, HeaderSize, body.Length); return data; }
// returns short XML name of message for logging private static string _getTag(BplMessage message, out string id) { id = ""; if (message == null) return null; Func<OscarSessionData, string> getId = s => { if (s == null) return ""; if (s.VehicleId.IsEmpty || (s.VehicleId.LocalId as string) == null) return s.VehicleSN; if ((s.VehicleId.LocalId as string).StartsWith("Factory")) return s.VehicleSN; return s.VehicleId.LocalId as string; }; Func<BplObject, string> getTag = o => { var prefix = BplLanguage.Schemas.Contains(o.Class.Schema) ? BplLanguage.Schemas[o.Class.Schema] : ""; return "{0}:{1}".Substitute(prefix, o.Class.TagName.LocalName); }; if (message.IsA<OscarRequestWrapper>()) { id = getId(((OscarRequestWrapper)message).SessionData); return getTag(((OscarRequestWrapper)message).Input ?? message); } else if (message.IsA<OscarResponseWrapper>()) { id = getId(((OscarResponseWrapper)message).SessionData); return getTag(((OscarResponseWrapper)message).Output ?? message); } else if (message.IsA<OscarFailureWrapper>()) { id = getId(((OscarFailureWrapper)message).SessionData); return getTag(((OscarFailureWrapper)message).Error ?? message); } else { id = ((BplLegacyMessage)message).ClientId.LocalId as string; return getTag(message); } }
private static void _logException(Exception exception, BplMessage message, string extra, bool isExpected) { if (exception == null) return; var dump = "(unknown)"; try { if (message != null) { var formatter = BplXmlFormatter.Minimal; if (formatter.Format(message)) dump = formatter.Output; } } catch { } // Log.Exception is not used because it dumps stack after message, so it's far at the end of the line and not usable // Log.Warn is used (and not Info), to be able to filter out exceptions, from IN/OUT // Log.Error is not used because large number of wrong messages will kill the logfile (in case log is configured to Error and expected to be mostly OFF) // Log.Verbose is used for 'isExpected' exceptions, to be able to dump (and later exclude) comm exception thrown by other threads, when one thread closes connection // Explicit severity checks are done to prevent FormatException from running, when the desired severity is excluded from the log if (isExpected) { if (Log.FileSeverity <= Severity.Verbose) Log.Verbose("{0}. Message: {1}. Extra: {2}", Log.FormatException(exception), dump, extra); } else { if (Log.FileSeverity <= Severity.Warning) Log.Warn("{0}. Message: {1}. Extra: {2}", Log.FormatException(exception), dump, extra); } }
private static void _logMessage(string dir, int len, BplMessage message, BplMessage request) { if (!CommunicationManager.LogMessages || message == null) return; // filtering is done by request, if request != null, then message is the response, so request is used var originalRequest = request ?? message; var filterable = _unwrapMessage(originalRequest); if (filterable == null) return; var role = originalRequest.IsA<OscarRequestWrapper>() ? (((OscarRequestWrapper)originalRequest).SessionData != null ? (((OscarRequestWrapper)originalRequest).SessionData).ClientRole : null) : null; if (_excludedLogRoles.Contains(role)) return; if (_excludedLogMessages.Contains(filterable.Class)) return; if (_excludedLogPatterns.Any(p => filterable.Class.CanonicName.StartsWith(p))) return; var formatter = BplXmlFormatter.Minimal; var log = formatter.Format(message) ? formatter.Output : "Failed to serialize '{0}'. {1}".Substitute(message.Class.Name, formatter.Errors.ToString()); try { if (!LogSessionData) log = Regex.Replace(log, "<sessionData>(.*?)</sessionData>", ""); if (!LogNamespaces) log = Regex.Replace(log, "\\sxmlns[^\"]+\"[^\"]+\"", ""); } catch { }; var id = ""; var name = ""; if (message.IsA<OscarRequestWrapper>()) { name = _getTag(message, out id); } else if (message.IsA<OscarResponseWrapper>()) { name = (request != null) ? _getTag(request, out id) + "_Result" : _getTag(message, out id); } else if (message.IsA<OscarFailureWrapper>()) { name = (request != null) ? _getTag(request, out id) + "_Failure" : _getTag(message, out id); } else if (message.IsA<BplLegacyMessage>()) { name = _getTag(message, out id); } Log.Record<CommunicationManager>("{0,-3}; {1,5}; {2,18}; {3}; {4}", dir, len, id, name, log); }
public void SendResponse(BplMessage request, BplMessage response, object state) { var c = state as Connection; if (c != null && response != null) { c.Outbox.Add(response, request); c.NextAction(); } }
public void SendRequest(Uri uri, BplMessage request, Action<BplContextNode> onResponse, Action<GeneralFailure> onFailure) { if (uri == null) { var formatter = BplXmlFormatter.Minimal; var info = formatter.Format(request) ? formatter.Output : "(unknown)"; throw new BplServiceHandlerException("Unspecified URI invoking request: " + info); } var entry = onResponse != null ? new InboxEntry { Request = request, ResponseHandler = onResponse, FailureHandler = onFailure } : null; // connection state is deliberatelly not checked, because the connection can be in BeginConnect-ing state // in which there's no problem to add to Outbox/Inbox and call NextAction (which will do nothing, // because _canWrite/_canRead are false, and the sending will be done later, when EndConnect happens). // Besides, this is designed to well-connected clients, so polling socket for no real reason is not a good idea. // var c = _outConnections.Find(uri); if (c == null) { c = new Connection(); c.BeginConnect(uri); } else { if (c.Outbox.RequestCount >= CommunicationManager.MaxOutgoingRequests) { Log.Warn("Outgoing requests queue is full ({0} items)", c.Outbox.RequestCount); if (onFailure != null) onFailure(new GeneralFailure(GeneralFailureCode.ServiceUnavailable)); return; } } c.Outbox.Add(request, null); c.Inbox.Add(entry); c.NextAction(); }