/** * 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 possible delimiter between the server & application and the stream path and file. */ private static final String WOWZA_STREAM_DELIMITER = "_definst_"; @Override public String getResource(String baseUri) { try { URI uri = new URI(baseUri); String scheme = uri.getScheme(); if (RTMP_SCHEME.equals(scheme)) { return getRTMPResource(uri); } else { 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() : ""); } }