View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  package org.opencastproject.security.urlsigning;
22  
23  import org.opencastproject.urlsigning.common.ResourceStrategy;
24  
25  import org.apache.commons.lang3.StringUtils;
26  
27  import java.net.URI;
28  import java.net.URISyntaxException;
29  import java.util.regex.Matcher;
30  import java.util.regex.Pattern;
31  
32  /**
33   * A {@link ResourceStrategy} that transforms URLs for a Wowza streaming server. Based upon:
34   * http://www.wowza.com/forums/content.php?55-How-to-format-Adobe-Flash-RTMP-URLs and http://docs.aws.amazon.com/
35   * AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-custom-policy.html
36   */
37  public class WowzaResourceStrategyImpl implements ResourceStrategy {
38    /** Regex pattern that matches something like mp4:path/to/resource/video */
39    private static final String FIND_STREAM_FORMAT = ".{3}[:].*$";
40    /** The URI scheme that an RTMP address uses. */
41    private static final String RTMP_SCHEME = "rtmp";
42    /** The URI scheme that an http address uses. */
43    private static final String ADAPTIVESTREAMING_HTTP_SCHEME = "http";
44    /** The URI scheme that an https address uses. */
45    private static final String ADAPTIVESTREAMING_HTTPS_SCHEME = "https";
46    /** The possible delimiter between the server & application and the stream path and file. */
47    private static final String WOWZA_STREAM_DELIMITER = "_definst_";
48  
49    /**
50     * Transform a base URI into a proper stream location without the host and application name.
51     *
52     * @param baseUri
53     *          The full URI to the resource including the host and application.
54     * @return A safe standard RTMP or HTTP resource location.
55     */
56    @Override
57    public String getResource(String baseUri) {
58      try {
59        URI uri = new URI(baseUri);
60        String scheme = uri.getScheme();
61  
62        if (ADAPTIVESTREAMING_HTTP_SCHEME.equals(scheme)
63            || ADAPTIVESTREAMING_HTTPS_SCHEME.equals(scheme)) {
64          return getHTTPResource(uri);
65        } else if (RTMP_SCHEME.equals(scheme)) {
66          return getRTMPResource(uri);
67        } else {
68          //return getHTTPResource(uri);
69          throw new IllegalArgumentException(WowzaResourceStrategyImpl.class.getSimpleName()
70                  + " is unable to sign urls with scheme " + scheme);
71        }
72      } catch (URISyntaxException e) {
73        throw new IllegalStateException(e);
74      }
75    }
76  
77    /**
78     * Transform a base URI into a proper stream location without the host and application name.
79     *
80     * @param baseUri
81     *          The full URI to the resource including the host and application.
82     * @return A safe standard RTMP resource location.
83     */
84    protected static String getRTMPResource(URI baseUri) {
85      String stream = null;
86      if (baseUri.toString().contains(WOWZA_STREAM_DELIMITER)) {
87        // There is the explicit delimiter so return the stream as the resource.
88        stream = baseUri.toString().split(WOWZA_STREAM_DELIMITER)[1];
89        if (stream.charAt(0) == '/') {
90          return stream.substring(1);
91        }
92        return stream;
93      } else if (baseUri.getPath().contains(":")) {
94        // The path contains a ":" denoting the type e.g. mp4 which is always the start of the stream path.
95        Pattern pattern = Pattern.compile(FIND_STREAM_FORMAT);
96        Matcher matcher = pattern.matcher(baseUri.getPath());
97        if (matcher.find()) {
98          return baseUri.getPath().substring(matcher.start())
99                  + (StringUtils.isNotBlank(baseUri.getQuery()) ? "?" + baseUri.getQuery() : "");
100       }
101     }
102     // There are no special delimiters so assume the first value between two forward slashes (/.../) is the application.
103     return baseUri.getPath().substring(baseUri.getPath().indexOf("/", 1) + 1)
104             + (StringUtils.isNotBlank(baseUri.getQuery()) ? "?" + baseUri.getQuery() : "");
105   }
106   /**
107    * Transform a base URI into a proper stream location without the host.
108    *
109    * @param baseUri
110    *          The full URI to the resource including the host and application.
111    * @return A safe standard RTMP resource location.
112    */
113   protected static String getHTTPResource(URI baseUri) {
114     // There are no special delimiters so assume the first value between two forward slashes (/.../) is the application.
115     return baseUri.getPath().substring(baseUri.getPath().indexOf("/") + 1, baseUri.getPath().lastIndexOf("/"));
116   }
117 }