1/*****************************************************
2*
3*  Copyright 2009 Adobe Systems Incorporated.  All Rights Reserved.
4*
5*****************************************************
6*  The contents of this file are subject to the Mozilla Public License
7*  Version 1.1 (the "License"); you may not use this file except in
8*  compliance with the License. You may obtain a copy of the License at
9*  http://www.mozilla.org/MPL/
10*
11*  Software distributed under the License is distributed on an "AS IS"
12*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
13*  License for the specific language governing rights and limitations
14*  under the License.
15*
16*
17*  The Initial Developer of the Original Code is Adobe Systems Incorporated.
18*  Portions created by Adobe Systems Incorporated are Copyright (C) 2009 Adobe Systems
19*  Incorporated. All Rights Reserved.
20*
21*  Contributor(s): Akamai Technologies
22*
23*****************************************************/
24package org.osmf.net
25{
26	import __AS3__.vec.Vector;
27
28	import org.osmf.utils.URL;
29
30	[ExcludeClass]
31
32	/**
33	 * @private
34	 *
35	 * Parses a URL into properties specific to Flash Media Server.
36	 *
37	 * @see URL
38	 *
39	 *  @langversion 3.0
40	 *  @playerversion Flash 10
41	 *  @playerversion AIR 1.5
42	 *  @productversion OSMF 1.0
43	 */
44	public class FMSURL extends URL
45	{
46		/**
47		 * Set the URL this class will work with.
48		 *
49		 * @param url The URL this class will use to provide FMS-specific information such as app name and instance name.
50		 * @param useInstance If true, then the second part of the URL path is considered the instance name,
51		 * such as <code>rtmp://host/app/foo/bar/stream</code>. In this case the instance name would be 'foo' and the stream would
52		 * be 'bar/stream'.
53		 * If false, then the second part of the URL path is considered to be the stream name,
54		 * such as <code>rtmp://host/app/foo/bar/stream</code>. In this case there is no instance name and the stream would
55		 * be 'foo/bar/stream'.
56		 *
57		 *
58		 *  @langversion 3.0
59		 *  @playerversion Flash 10
60		 *  @playerversion AIR 1.5
61		 *  @productversion OSMF 1.0
62		 */
63		public function FMSURL(url:String, useInstance:Boolean=false)
64		{
65			super(url);
66			_useInstance = useInstance;
67			_appName = "";
68			_instanceName = "";
69			_streamName = "";
70			_fileFormat = "";
71
72			parsePath();
73			parseQuery();
74		}
75
76		/**
77		 * Whether a named instance is being used within the URI
78		 *
79		 *  @langversion 3.0
80		 *  @playerversion Flash 10
81		 *  @playerversion AIR 1.5
82		 *  @productversion OSMF 1.0
83		 */
84		public function get useInstance():Boolean
85		{
86			return _useInstance;
87		}
88
89		/**
90		 * The FMS application name.
91		 *
92		 *  @langversion 3.0
93		 *  @playerversion Flash 10
94		 *  @playerversion AIR 1.5
95		 *  @productversion OSMF 1.0
96		 */
97		public function get appName():String
98		{
99			return _appName;
100		}
101
102		/**
103		 * The FMS instance name.
104		 *
105		 *  @langversion 3.0
106		 *  @playerversion Flash 10
107		 *  @playerversion AIR 1.5
108		 *  @productversion OSMF 1.0
109		 */
110		public function get instanceName():String
111		{
112			return _instanceName;
113		}
114
115		/**
116		 * The FMS stream name.
117		 *
118		 *  @langversion 3.0
119		 *  @playerversion Flash 10
120		 *  @playerversion AIR 1.5
121		 *  @productversion OSMF 1.0
122		 */
123		public function get streamName():String
124		{
125			return _streamName;
126		}
127
128		/**
129		 * The file format of the streaming media.  Corresponds to one of the
130		 * public constants defined in this class, such as MP4_STREAM,
131		 * or the blank stream for flv media streams.
132		 *
133		 *  @langversion 3.0
134		 *  @playerversion Flash 10
135		 *  @playerversion AIR 1.5
136		 *  @productversion OSMF 1.0
137		 */
138		public function get fileFormat():String
139		{
140			return _fileFormat;
141		}
142
143		/**
144		 * The vector of edges.
145		 *
146		 * @see FMSHost
147		 *
148		 *  @langversion 3.0
149		 *  @playerversion Flash 10
150		 *  @playerversion AIR 1.5
151		 *  @productversion OSMF 1.0
152		 */
153		public function get edges():Vector.<FMSHost>
154		{
155			return _edges;
156		}
157
158		/**
159		 * The vector of origins.
160		 *
161		 * @see FMSHost
162		 *
163		 *  @langversion 3.0
164		 *  @playerversion Flash 10
165		 *  @playerversion AIR 1.5
166		 *  @productversion OSMF 1.0
167		 */
168		public function get origins():Vector.<FMSHost>
169		{
170			return _origins;
171		}
172
173		/**
174		 * Parse the path in the URL object into FMS specific properties.
175		 * The path is everything after the host but before any query string parameters, with no leading or trailing slashes.
176		 * <p>
177		 * For example, in this URL: <code>"http://host.com:80/foo/bar/index.html?a=1&#38;b=2"</code>
178		 * path would be <code>"foo/bar/index.html"</code></p>
179		 *
180		 *  @langversion 3.0
181		 *  @playerversion Flash 10
182		 *  @playerversion AIR 1.5
183		 *  @productversion OSMF 1.0
184		 */
185		private function parsePath():void
186		{
187			if ((path == null) || (path.length == 0))
188			{
189				// Check the query string for a stream name since the path is empty
190				_streamName = getParamValue(QUERY_STRING_STREAM);
191				// Check the query string for stream type since the path is empty
192				_fileFormat = getParamValue(QUERY_STRING_STREAMTYPE);
193				return;
194			}
195
196 			var pattern:RegExp = /(\/)/;
197 			var result:Array = path.split(pattern);
198
199 			if (result != null)
200 			{
201	 			_appName = result[APPNAME_START_INDEX];
202	 			_instanceName = "";
203	 			_streamName = "";
204
205	 			// If "_definst_" is in the path and in the right place, we'll assume everything after that is the stream
206		 		var definstPattern:RegExp = new RegExp("^.*\/" + DEFAULT_INSTANCE_NAME, "i");
207
208	 			if (path.search(definstPattern) > -1)
209	 			{
210	 				_useInstance = true;
211	 			}
212
213	 			var streamStartNdx:uint = STREAMNAME_START_INDEX;
214
215	 			if (_useInstance)
216	 			{
217	 				_instanceName = result[INSTANCENAME_START_INDEX];
218	 			}
219	 			else
220	 			{
221	 				streamStartNdx = INSTANCENAME_START_INDEX;
222	 			}
223
224	 			for (var i:int = streamStartNdx; i < result.length; i++)
225	 			{
226	 				_streamName += result[i];
227	 			}
228
229	 			// If no streamName found in the path, check the query string
230	 			if (_streamName == null || _streamName == "")
231	 			{
232					_streamName = getParamValue(QUERY_STRING_STREAM);
233				}
234
235	 			if (_streamName.search(/^mp4:/i) > -1)
236	 			{
237	 				_fileFormat = MP4_STREAM;
238	 			}
239	 			else if (_streamName.search(/^mp3:/i) > -1)
240	 			{
241	 				_fileFormat = MP3_STREAM;
242	 			}
243	 			else if (_streamName.search(/^id3:/i) > -1)
244	 			{
245	 				_fileFormat = ID3_STREAM;
246	 			}
247
248	 			// If no stream type found check the query string
249	 			if (_fileFormat == null || _fileFormat == "")
250	 			{
251	 				_fileFormat = getParamValue(QUERY_STRING_STREAMTYPE);
252	 			}
253 			}
254		}
255
256		/**
257		 * Parse the query string for origin/edge info.
258		 * A sample FMS URI with origin/edge info in the query string might look like this:
259		 * "rtmp://edge1/?rtmp://edge2/?rtmp://origin/app/inst/mp4:foldera/folder/b/myfile.mp4"
260		 *
261		 *  @langversion 3.0
262		 *  @playerversion Flash 10
263		 *  @playerversion AIR 1.5
264		 *  @productversion OSMF 1.0
265		 */
266		private function parseQuery():void
267		{
268			// If there is no query string or there are no protocols in the query string, there is nothing to do
269			if (query == null || query.length == 0 || (query.search(/:\//) == -1))
270			{
271				return;
272			}
273
274			var edgeOriginURIs:Array = query.split("?");
275
276			// Remove the items that don't have a protocol
277			for (var ndx:int = 0; ndx < edgeOriginURIs.length; ndx++)
278			{
279		 		var tempIndex:int = edgeOriginURIs[ndx].toString().search(/:\//);
280		 		if (tempIndex == -1)
281		 		{
282		 			edgeOriginURIs.splice(ndx, 1);
283		 		}
284			}
285
286			var hasEdge:Boolean = false;
287			var originIndex:int = 0;
288
289			// if it splits into more than one item, we assume it has an edge and the last one is the origin
290			if (edgeOriginURIs.length >= 2) {
291				hasEdge = true;
292				originIndex = edgeOriginURIs.length -1;
293			}
294
295			var tempSN:String = "";  // temporary server name
296			var tempPN:String = "";	// temporary port number
297			var colonIndex:int = 0;
298			var slashIndex:int = 0;
299			var startIndex:int = 0;
300			var endIndex:int = 0;
301
302			for (var i:int = 0; i < edgeOriginURIs.length; i++)
303			{
304
305		 		var tempNdex:int = edgeOriginURIs[i].toString().search(/:\//);
306
307		 		startIndex = tempNdex + 2;
308
309				if (edgeOriginURIs[i].charAt(startIndex) == '/')
310				{
311					// if not local URI (i.e. rtmp:/app/) then move index up
312					startIndex++;
313				}
314
315				// get server (and maybe port)
316				colonIndex = edgeOriginURIs[i].indexOf(":", startIndex);
317				slashIndex = edgeOriginURIs[i].indexOf("/", startIndex);
318
319				if (slashIndex < 0 && colonIndex < 0)
320				{
321					tempSN = edgeOriginURIs[i].slice(startIndex);
322				}
323				else if (colonIndex >= 0 && colonIndex < slashIndex)
324				{
325					endIndex = colonIndex;
326					tempSN = edgeOriginURIs[i].slice(startIndex, endIndex);
327					startIndex = endIndex + 1;
328					endIndex = slashIndex;
329					tempPN = edgeOriginURIs[i].slice(startIndex, endIndex);
330				}
331				else if (edgeOriginURIs[i].indexOf("://") != -1)
332				{
333					endIndex = slashIndex;
334					tempSN = edgeOriginURIs[i].slice(startIndex, endIndex);
335				}
336				else
337				{
338					endIndex = edgeOriginURIs[i].indexOf("/");
339					tempSN = "localhost";
340				}
341
342				// if it's the origin, we need to push the origin and get the app and stream name
343				if (i == originIndex)
344				{
345					if (_origins == null)
346					{
347						_origins = new Vector.<FMSHost>;
348					}
349					_origins.push(new FMSHost(tempSN, tempPN));
350
351					var tempFMSURL:FMSURL = new FMSURL(edgeOriginURIs[i], _useInstance);
352
353					if (_appName == "")
354					{
355						_appName = tempFMSURL.appName;
356					}
357
358					if (_useInstance && _instanceName == "")
359					{
360						_instanceName = tempFMSURL.instanceName;
361					}
362
363					if (_streamName == "")
364					{
365						_streamName = tempFMSURL.streamName;
366					}
367				}
368				else if((edgeOriginURIs[i] != query) && hasEdge)
369				{
370					if (_edges == null)
371					{
372						_edges = new Vector.<FMSHost>;
373					}
374					_edges.push(new FMSHost(tempSN, tempPN));
375				}
376			}
377		}
378
379		private var _useInstance:Boolean;
380		private var _appName:String;
381		private var _instanceName:String;
382		private var _streamName:String;
383		private var _fileFormat:String;
384		private var _origins:Vector.<FMSHost>;
385		private var _edges:Vector.<FMSHost>;
386
387		private static const APPNAME_START_INDEX:uint = 0;
388		private static const INSTANCENAME_START_INDEX:uint = 2;
389		private static const STREAMNAME_START_INDEX:uint = 4;
390
391		private static const DEFAULT_INSTANCE_NAME:String = "_definst_";
392
393		public static const MP4_STREAM:String = "mp4";
394		public static const MP3_STREAM:String = "mp3";
395		public static const ID3_STREAM:String = "id3";
396
397		public static const QUERY_STRING_STREAM:String = "streamName";
398		public static const QUERY_STRING_STREAMTYPE:String = "streamType";
399	}
400}
401