// 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 });
    }
 }
Exemple #2
0
      /// <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();
      }