protected void OnMessage(PlainEventMsg msg) { NameValueCollection parameters = msg.ParseBody(true); string eventName = parameters["event-name"]; if (eventName == null) { return; } eventName = string.Compare(parameters["event-name"], "custom", true) == 0 ? parameters["event-subclass"] : parameters["event-name"]; //Quickhack for Sofia::Register (convert to sofia_register) eventName = eventName.Replace("::", "_"); eventName = StringHelper.UpperCaseToCamelCase(eventName); // Try to load the event (check if we've created an event class for the registered event) EventBase eb = EventBase.CreateEvent(eventName); // Yep, we got an event class. Let it parse the event information if (eb != null) { eb.SetParameters(parameters); eb.Parse(parameters); EventReceived(eb); } else if (DisplayMissingEventTypes) { _logWriter(LogPrio.Warning, "Failed to load '" + eventName + "'."); } }
internal static void ParseHeaders(PlainEventMsg msg, string header) { StringParser parser = new StringParser(header); string name = parser.Read(':', true); while (name != string.Empty) { switch (name) { case "Content-Length": msg.ContentLength = int.Parse(parser.ReadLine(true)); break; case "Content-Type": msg.ContentType = parser.ReadLine(true); break; case "Reply-Text": msg.Body = parser.ReadLine(true); break; } // empty line == end of header if (parser.EOL) { break; } name = parser.Read(':', true); } }
private PlainEventMsg FindHeaders(ref int index) { IgnoreEmptyLines(ref index); int start = index; FindEmptyLine(ref index); if (start == index) { return(null); //got no header. } char[] headerBuffer = new char[index - start]; _text.CopyTo(start, headerBuffer, 0, index - start); PlainEventMsg eventMsg = new PlainEventMsg(); ParseHeaders(eventMsg, new string(headerBuffer)); return(eventMsg); }
// Should ONLY be called from the parsing thread. private void ParseMessages() { PlainEventMsg msg = _parser.ParseOne(); while (msg != null) { LogWriter(LogPrio.Trace, "MessageType: " + msg.ContentType); switch (msg.ContentType) { case "auth/request": { var cmd = new AuthCommand(_password); cmd.ReplyReceived += OnAuthed; _commands.Enqueue(cmd); Write(cmd + "\n\n"); } break; case "api/response": case "command/reply": if (_commands.Count > 0) { CmdBase cmd = _commands.Dequeue(); cmd.HandleReply(cmd.CreateReply(msg.Body.Trim())); } else { LogWriter(LogPrio.Debug, "Got command reply or api response, but no actual command/api: " + msg.Body); } break; default: MessageReceived(msg); break; } msg = _parser.ParseOne(); } }
/// <summary> /// /// </summary> /// <returns></returns> /// <exception cref="InvalidDataException">If parsing failed due to invalid format.</exception> public PlainEventMsg ParseOne() { // Move queue to text lock (_piecesToAppend) { while (_piecesToAppend.Count > 0) { _text.Append(_piecesToAppend.Dequeue()); } } if (_text.Length == 0) { return(null); } PlainEventMsg plainEvent; lock (_text) { int i = 0; IgnoreLineBreaks(ref i); _text.Remove(0, i); i = FindEmptyLine(i); if (i == -1) { return(null); } // extract header char[] chars = new char[i]; _text.CopyTo(0, chars, 0, i); string headers = new string(chars); IgnoreLineBreaks(ref i); plainEvent = new PlainEventMsg(); ParseHeaders(plainEvent, headers); // we got a body? if (plainEvent.ContentLength > 0) { // Start of Empty header bugfix // FS seems to send a header with content-length without sending an actual body // this will eat that kind of header. if (_text.Length >= BugWorkaround.Length + i) { bool found = true; for (int index = 0; index < BugWorkaround.Length; ++index) { if (_text[index + i] != BugWorkaround[index]) { found = false; break; } } if (found) { _text.Remove(0, i); Console.WriteLine("Removing empty content header."); return(null); } } // end of bugfix. // not enough data for body. if (plainEvent.ContentLength + i > _text.Length) { return(null); } // extract body // since Freeswitch report false sizes sometimes, // we need to go through all lines to find contents. // // If FreeSWITCH is actually doing something wrong and you can prove it, you should report it as a bug. // More likely you are misunderstanding the protocol and confusing the events with more encapsulated events // as a mistake. Dozens of other people have had that same mistake making an ESL client. // You should look in libs/esl and try to get the swig wrapper on the esl lib working instead. int endOfBody = FindEmptyLine(i) + 2; // include the first and last \n if (endOfBody - i != plainEvent.ContentLength) { Console.WriteLine("Invalid content length, actual length: " + (endOfBody - i) + ", reported length: " + plainEvent.ContentLength); plainEvent.ContentLength = endOfBody - i; } chars = new char[plainEvent.ContentLength]; _text.CopyTo(i, chars, 0, plainEvent.ContentLength); plainEvent.Body = new string(chars); // api/response is buggy for originate, no \n\n are appended at the end. if (plainEvent.ContentType != "api/response") { // check for errors. int pos = plainEvent.Body.IndexOf("\n\n"); if (pos < plainEvent.Body.Length - 2) { Console.WriteLine("Invalid event"); Console.WriteLine("Header"); Console.WriteLine(headers); Console.WriteLine("Body"); Console.WriteLine(plainEvent.Body); Console.WriteLine("=========================== EVERYTHING unparsed =============================="); Console.WriteLine(_text); throw new InvalidDataException("Invalid event: " + _text); } } if (plainEvent.Body.Length < plainEvent.ContentLength) { throw new InvalidDataException("Body contents are too small!"); } // Move forward to next header i += plainEvent.ContentLength; IgnoreLineBreaks(ref i); } // remove header( + body) from buffer _text.Remove(0, i); } return(plainEvent); }
public PlainEventMsg ParseOne() { AppendPieces(); int index = 0; PlainEventMsg msg = FindHeaders(ref index); if (msg == null) { return(null); } // no body if (msg.ContentLength <= 0) { _text.Remove(0, index); return(msg); } // too few bytes. if (_text.Length - index < msg.ContentLength) { return(null); } // ignore empty line between header and body. IgnoreEmptyLines(ref index); int start = index; FindEmptyLine(ref index); int size = index - start + 2; // include \n\n // Validate size. // // Someone suggested that the sizes are correct and that // the events was wrapped. Well. I've checked if the // specified position is a \n\n, and it's not. // // Don't force me to check source code of another implementation // if (size != msg.ContentLength) { // Check if the buffer contains header + body only, // then use the reported size, despite that our check failed. if (_text.Length == msg.ContentLength + start) { size = msg.ContentLength; } // buffer contains more than one message. else if (_text.Length > msg.ContentLength + start) { // Check if the reported size ends with a \n, // in that case we have a container event. if (_text[msg.ContentLength + start - 1] == '\n') { size = msg.ContentLength; } } } char[] body = new char[size]; _text.CopyTo(start, body, 0, size); msg.Body = new string(body); _text.Remove(0, index); return(msg); }