using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Globalization; using System.IO; using System.Text; using System.Text.RegularExpressions; namespace Inspectron.HawkEye.RTSP.Messages { public class RtspMessage : RtspChunk { private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger(); /// /// The regex to validate the Rtsp message. /// private static readonly Regex _rtspVersionTest = new Regex(@"^RTSP/\d\.\d", RegexOptions.Compiled); /// /// Create the good type of Rtsp Message from the header. /// /// A request line. /// An Rtsp message public static RtspMessage GetRtspMessage(string aRequestLine) { // We can't determine the message if (string.IsNullOrEmpty(aRequestLine)) return new RtspMessage(); string[] requestParts = aRequestLine.Split(new char[] { ' ' }, 3); RtspMessage returnValue; if (requestParts.Length == 3) { // A request is : Method SP Request-URI SP RTSP-Version // A response is : RTSP-Version SP Status-Code SP Reason-Phrase // RTSP-Version = "RTSP" "/" 1*DIGIT "." 1*DIGIT if (_rtspVersionTest.IsMatch(requestParts[2])) returnValue = RtspRequest.GetRtspRequest(requestParts); else if (_rtspVersionTest.IsMatch(requestParts[0])) returnValue = new RtspResponse(); else { _logger.Warn(CultureInfo.InvariantCulture, "Got a strange message {0}", aRequestLine); returnValue = new RtspMessage(); } } else { _logger.Warn(CultureInfo.InvariantCulture, "Got a strange message {0}", aRequestLine); returnValue = new RtspMessage(); } returnValue.Command = aRequestLine; return returnValue; } /// /// Initializes a new instance of the class. /// public RtspMessage() { Data = new byte[0]; Creation = DateTime.Now; } private Dictionary _headers = new Dictionary(StringComparer.OrdinalIgnoreCase); internal protected string[] commandArray; /// /// Gets or sets the creation time. /// /// The creation time. public DateTime Creation { get; private set; } /// /// Gets or sets the command of the message (first line). /// /// The command. public string Command { get { if (commandArray == null) return string.Empty; return string.Join(" ", commandArray); } set { if (value == null) commandArray = new string[] { String.Empty }; else commandArray = value.Split(new char[] {' '}, 3); } } /// /// Gets the Method of the message (eg OPTIONS, DESCRIBE, SETUP, PLAY). /// /// The Method public string Method { get { if (commandArray == null) return string.Empty; return commandArray[0]; } } /// /// Gets the headers of the message. /// /// The headers. public Dictionary Headers { get { return _headers; } } /// /// Adds one header from a string. /// /// The string containing header of format Header: Value. /// is null public void AddHeader(string line) { if (line == (string)null) throw new ArgumentNullException("line"); //spliter string[] elements = line.Split(new char[] { ':' }, 2); if (elements.Length == 2) { _headers[elements[0].Trim()] = elements[1].TrimStart(); } else { _logger.Warn(CultureInfo.InvariantCulture, "Invalid Header received : -{0}-", line); } } /// /// Gets or sets the Ccommande Seqquence number. /// If the header is not define or not a valid number it return 0 /// /// The sequence number. public int CSeq { get { string returnStringValue; int returnValue; if (!(_headers.TryGetValue("CSeq", out returnStringValue) && int.TryParse(returnStringValue, out returnValue))) returnValue = 0; return returnValue; } set { _headers["CSeq"] = value.ToString(CultureInfo.InvariantCulture); } } /// /// Gets the session ID. /// /// The session ID. public virtual string Session { get { if (!_headers.ContainsKey("Session")) return null; return _headers["Session"]; } set { _headers["Session"] = value; } } /// /// Initialises the length of the data byte array from content lenth header. /// public void InitialiseDataFromContentLength() { int dataLength; if (!(_headers.ContainsKey("Content-Length") && int.TryParse(_headers["Content-Length"], out dataLength))) { dataLength = 0; } this.Data = new byte[dataLength]; } /// /// Adjusts the content length header. /// public void AdjustContentLength() { if (Data.Length > 0) { _headers["Content-Length"] = Data.Length.ToString(CultureInfo.InvariantCulture); } else { _headers.Remove("Content-Length"); } } /// /// Sends to the message to a stream. /// /// The stream. /// is empty /// can't be written. public void SendTo(Stream stream) { // if (stream == null) throw new ArgumentNullException("stream"); if (!stream.CanWrite) throw new ArgumentException("Stream CanWrite == false, can't send message to it", "stream"); // Contract.EndContractBlock(); Encoding encoder = ASCIIEncoding.UTF8; StringBuilder outputString = new StringBuilder(); AdjustContentLength(); // output header outputString.Append(Command); outputString.Append("\r\n"); foreach (KeyValuePair item in _headers) { outputString.AppendFormat("{0}: {1}\r\n", item.Key, item.Value); } outputString.Append("\r\n"); byte[] buffer = encoder.GetBytes(outputString.ToString()); lock(stream) { stream.Write(buffer, 0, buffer.Length); // Output data if (Data.Length > 0) stream.Write(Data, 0, Data.Length); } stream.Flush(); } /// /// Logs the message. /// /// A log level. public override void LogMessage(NLog.LogLevel aLevel) { // Default value to debug if (aLevel == null) aLevel = NLog.LogLevel.Debug; // if the level is not logged directly return if (!_logger.IsEnabled(aLevel)) return; _logger.Log(aLevel, "Commande : {0}", Command); foreach (KeyValuePair item in _headers) { _logger.Log(aLevel, "Header : {0}: {1}", item.Key, item.Value); } if (Data.Length > 0) { _logger.Log(aLevel, "Data :-{0}-", ASCIIEncoding.ASCII.GetString(Data)); } } /// /// Crée un nouvel objet qui est une copie de l'instance en cours. /// /// /// Nouvel objet qui est une copie de cette instance. /// public override object Clone() { RtspMessage returnValue = GetRtspMessage(this.Command); foreach (var item in this.Headers) { if (item.Value == null) returnValue.Headers.Add(item.Key.Clone() as string, null); else returnValue.Headers.Add(item.Key.Clone() as string, item.Value.Clone() as string); } returnValue.Data = this.Data.Clone() as byte[]; returnValue.SourcePort = this.SourcePort; return returnValue; } } }