WowzaResourceStrategyImpl.java

/*
 * Licensed to The Apereo Foundation under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 *
 * The Apereo Foundation licenses this file to you under the Educational
 * Community License, Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of the License
 * at:
 *
 *   http://opensource.org/licenses/ecl2.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */
package org.opencastproject.security.urlsigning;

import org.opencastproject.urlsigning.common.ResourceStrategy;

import org.apache.commons.lang3.StringUtils;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * A {@link ResourceStrategy} that transforms URLs for a Wowza streaming server. Based upon:
 * http://www.wowza.com/forums/content.php?55-How-to-format-Adobe-Flash-RTMP-URLs and http://docs.aws.amazon.com/
 * AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-custom-policy.html
 */
public class WowzaResourceStrategyImpl implements ResourceStrategy {
  /** Regex pattern that matches something like mp4:path/to/resource/video */
  private static final String FIND_STREAM_FORMAT = ".{3}[:].*$";
  /** The URI scheme that an RTMP address uses. */
  private static final String RTMP_SCHEME = "rtmp";
  /** The URI scheme that an http address uses. */
  private static final String ADAPTIVESTREAMING_HTTP_SCHEME = "http";
  /** The URI scheme that an https address uses. */
  private static final String ADAPTIVESTREAMING_HTTPS_SCHEME = "https";
  /** The possible delimiter between the server & application and the stream path and file. */
  private static final String WOWZA_STREAM_DELIMITER = "_definst_";

  /**
   * Transform a base URI into a proper stream location without the host and application name.
   *
   * @param baseUri
   *          The full URI to the resource including the host and application.
   * @return A safe standard RTMP or HTTP resource location.
   */
  @Override
  public String getResource(String baseUri) {
    try {
      URI uri = new URI(baseUri);
      String scheme = uri.getScheme();

      if (ADAPTIVESTREAMING_HTTP_SCHEME.equals(scheme)
          || ADAPTIVESTREAMING_HTTPS_SCHEME.equals(scheme)) {
        return getHTTPResource(uri);
      } else if (RTMP_SCHEME.equals(scheme)) {
        return getRTMPResource(uri);
      } else {
        //return getHTTPResource(uri);
        throw new IllegalArgumentException(WowzaResourceStrategyImpl.class.getSimpleName()
                + " is unable to sign urls with scheme " + scheme);
      }
    } catch (URISyntaxException e) {
      throw new IllegalStateException(e);
    }
  }

  /**
   * Transform a base URI into a proper stream location without the host and application name.
   *
   * @param baseUri
   *          The full URI to the resource including the host and application.
   * @return A safe standard RTMP resource location.
   */
  protected static String getRTMPResource(URI baseUri) {
    String stream = null;
    if (baseUri.toString().contains(WOWZA_STREAM_DELIMITER)) {
      // There is the explicit delimiter so return the stream as the resource.
      stream = baseUri.toString().split(WOWZA_STREAM_DELIMITER)[1];
      if (stream.charAt(0) == '/') {
        return stream.substring(1);
      }
      return stream;
    } else if (baseUri.getPath().contains(":")) {
      // The path contains a ":" denoting the type e.g. mp4 which is always the start of the stream path.
      Pattern pattern = Pattern.compile(FIND_STREAM_FORMAT);
      Matcher matcher = pattern.matcher(baseUri.getPath());
      if (matcher.find()) {
        return baseUri.getPath().substring(matcher.start())
                + (StringUtils.isNotBlank(baseUri.getQuery()) ? "?" + baseUri.getQuery() : "");
      }
    }
    // There are no special delimiters so assume the first value between two forward slashes (/.../) is the application.
    return baseUri.getPath().substring(baseUri.getPath().indexOf("/", 1) + 1)
            + (StringUtils.isNotBlank(baseUri.getQuery()) ? "?" + baseUri.getQuery() : "");
  }
  /**
   * Transform a base URI into a proper stream location without the host.
   *
   * @param baseUri
   *          The full URI to the resource including the host and application.
   * @return A safe standard RTMP resource location.
   */
  protected static String getHTTPResource(URI baseUri) {
    // There are no special delimiters so assume the first value between two forward slashes (/.../) is the application.
    return baseUri.getPath().substring(baseUri.getPath().indexOf("/") + 1, baseUri.getPath().lastIndexOf("/"));
  }
}