blob: 5ca0bf8d1f49e499c57b206cc0dc4e3c6585cb3e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache 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://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
#include "URISupport.h"
#include <activemq/exceptions/ExceptionDefines.h>
#include <decaf/util/StringTokenizer.h>
#include <decaf/lang/exceptions/IllegalArgumentException.h>
#include <decaf/lang/System.h>
#include <decaf/net/URLEncoder.h>
#include <decaf/net/URISyntaxException.h>
#include <sstream>
using namespace std;
using namespace activemq;
using namespace activemq::util;
using namespace decaf::util;
using namespace decaf::net;
using namespace decaf::lang;
using namespace decaf::lang::exceptions;
////////////////////////////////////////////////////////////////////////////////
bool URISupport::isCompositeURI(const URI& uri) {
std::string ssp = stripPrefix(uri.getRawSchemeSpecificPart(), "//");
if (ssp.find_first_of('(') == 0 && checkParenthesis(ssp)) {
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
void URISupport::parseURL(const std::string& URI, decaf::util::Properties& properties) {
try {
StringTokenizer tokenizer(URI, ":/");
std::vector<std::string> tokens;
// Require that there be three tokens at the least, these are
// transport, url, port.
if (tokenizer.countTokens() < 3) {
throw decaf::lang::exceptions::IllegalArgumentException(__FILE__, __LINE__, (string("URISupport::parseURL - "
"Marlformed URI: ") + URI).c_str());
}
// First element should be the Transport Type, following that is the
// URL and any params.
properties.setProperty("transport.protocol", tokenizer.nextToken());
// Parse URL and Port as one item, optional params follow the ?
// and then each param set is delimited with & we extract first
// three chars as they are the left over ://
properties.setProperty("transport.uri", tokenizer.nextToken("&?").substr(3));
// Now get all the optional parameters and store them as properties
int count = tokenizer.toArray(tokens);
for (int i = 0; i < count; ++i) {
tokenizer.reset(tokens[i], "=");
if (tokenizer.countTokens() != 2) {
throw decaf::lang::exceptions::IllegalArgumentException(__FILE__, __LINE__, (string("URISupport::parseURL - "
"Marlformed Parameter = ") + tokens[i]).c_str());
}
// Get them in order, passing both as nextToken calls in the
// set Property can cause reversed order.
string key = tokenizer.nextToken();
string value = URISupport::replaceEnvValues(tokenizer.nextToken());
// Store this param as a property
properties.setProperty(key, value);
}
}
AMQ_CATCH_RETHROW(IllegalArgumentException)
AMQ_CATCH_EXCEPTION_CONVERT(Exception, IllegalArgumentException)
AMQ_CATCHALL_THROW(IllegalArgumentException)
}
////////////////////////////////////////////////////////////////////////////////
Properties URISupport::parseQuery(std::string query) {
try {
Properties options;
URISupport::parseQuery(query, &options);
return options;
}
AMQ_CATCH_RETHROW(IllegalArgumentException)
AMQ_CATCH_EXCEPTION_CONVERT(Exception, IllegalArgumentException)
AMQ_CATCHALL_THROW(IllegalArgumentException)
}
////////////////////////////////////////////////////////////////////////////////
void URISupport::parseQuery(std::string query, Properties* properties) {
try {
if (properties == NULL) {
throw IllegalArgumentException(__FILE__, __LINE__, "URISupport::parseQuery - Can't pass in a null properties object");
}
// strip the initial "?"
size_t pos = query.find_first_of("?");
if (pos != std::string::npos) {
query = query.substr(pos + 1);
}
// split the query into parameters
StringTokenizer tokenizer(query, "&");
std::vector<std::string> options;
tokenizer.toArray(options);
std::vector<std::string>::const_iterator iter = options.begin();
for (; iter != options.end(); ++iter) {
tokenizer.reset(*iter, "=");
std::string key = "";
std::string value = "";
if (tokenizer.countTokens() != 2) {
throw IllegalArgumentException(__FILE__, __LINE__, "URISupport::parseQuery - Invalid URI Option.");
}
// Get the Key
if (tokenizer.hasMoreTokens() != false) {
key = tokenizer.nextToken();
}
// Get the Value
if (tokenizer.hasMoreTokens() != false) {
value = URISupport::replaceEnvValues(tokenizer.nextToken());
}
// Store them.
properties->setProperty(key, value);
}
}
AMQ_CATCH_RETHROW(IllegalArgumentException)
AMQ_CATCH_EXCEPTION_CONVERT(Exception, IllegalArgumentException)
AMQ_CATCHALL_THROW(IllegalArgumentException)
}
////////////////////////////////////////////////////////////////////////////////
std::string URISupport::replaceEnvValues(const std::string& value) {
try {
// If it matches the first env var indicator then we validate that it is
// surrounded by an { and } bracket. once done we remove the inner value
// and look up the env var.
if (value.at(0) == '$') {
if (value.size() > 3 && value.at(1) != '{' && value.at(value.size() - 1) != '}') {
throw new decaf::lang::exceptions::IllegalArgumentException(__FILE__, __LINE__, "URISupport::replaceEnvValues - Invalid Env Var Syntax: %s",
value.c_str());
}
string var = value.substr(2, value.size() - 3);
var = decaf::lang::System::getenv(var);
if (var == "") {
throw new decaf::lang::exceptions::IllegalArgumentException(__FILE__, __LINE__, "URISupport::replaceEnvValues - Env Var not set: %s",
value.c_str());
}
return var;
}
return value;
}
AMQ_CATCH_RETHROW(IllegalArgumentException)
AMQ_CATCH_EXCEPTION_CONVERT(Exception, IllegalArgumentException)
AMQ_CATCHALL_THROW(IllegalArgumentException)
}
////////////////////////////////////////////////////////////////////////////////
std::string URISupport::createQueryString(const Properties& options) {
try {
if (options.isEmpty()) {
ostringstream rc;
bool first = true;
std::vector<std::pair<std::string, std::string> > values = options.toArray();
std::vector<std::pair<std::string, std::string> >::const_iterator iter = values.begin();
for (; iter != values.end(); ++iter) {
if (first) {
first = false;
} else {
rc << "&";
}
rc << URLEncoder::encode(iter->first) << "=" << URLEncoder::encode(iter->second);
}
return rc.str();
} else {
return "";
}
}
AMQ_CATCH_RETHROW(URISyntaxException)
AMQ_CATCH_EXCEPTION_CONVERT(Exception, URISyntaxException)
AMQ_CATCHALL_THROW(URISyntaxException)
}
////////////////////////////////////////////////////////////////////////////////
bool URISupport::checkParenthesis(const std::string& str) {
bool result = true;
if (str != "") {
int open = 0;
int closed = 0;
std::string::const_iterator iter = str.begin();
for (; iter != str.end(); ++iter) {
if (*iter == '(') {
open++;
} else if (*iter == ')') {
closed++;
}
}
result = open == closed;
}
return result;
}
////////////////////////////////////////////////////////////////////////////////
CompositeData URISupport::parseComposite(const URI& uri) {
CompositeData result;
result.setScheme(uri.getScheme());
string ssp = stripPrefix(uri.getSchemeSpecificPart(), "//");
parseComposite(uri, result, ssp);
result.setFragment(uri.getFragment());
return result;
}
////////////////////////////////////////////////////////////////////////////////
void URISupport::parseComposite(const URI& uri, CompositeData& rc, const std::string& ssp) {
std::string componentString;
std::string params;
if (!checkParenthesis(ssp)) {
throw URISyntaxException(__FILE__, __LINE__, "%s, Not a matching number of '(' and ')' parenthesis", uri.toString());
}
std::size_t p;
std::size_t intialParen = ssp.find("(");
if (intialParen == 0) {
rc.setHost(ssp.substr(0, intialParen));
p = rc.getHost().find("/");
if (p != string::npos) {
rc.setPath(rc.getHost().substr(p));
rc.setHost(rc.getHost().substr(0, p));
}
p = ssp.rfind(")");
componentString = ssp.substr(intialParen + 1, p - (intialParen + 1));
params = ssp.substr(p + 1);
} else {
componentString = ssp;
params = "";
}
LinkedList<std::string> components = splitComponents(componentString);
std::auto_ptr<Iterator<std::string> > iter(components.iterator());
while (iter->hasNext()) {
rc.getComponents().add(URI(iter->next()));
}
p = params.find("?");
if (p != string::npos) {
if (p > 0) {
rc.setPath(stripPrefix(params.substr(0, p), "/"));
}
rc.setParameters(parseQuery(params.substr(p + 1)));
} else {
if (params.length() > 0) {
rc.setPath(stripPrefix(params, "/"));
}
}
}
////////////////////////////////////////////////////////////////////////////////
LinkedList<std::string> URISupport::splitComponents(const std::string& str) {
LinkedList<std::string> components;
std::size_t last = 0;
int depth = 0;
std::string::const_iterator iter = str.begin();
for (std::size_t i = 0; iter != str.end(); ++iter, ++i) {
switch (*iter) {
case '(':
depth++;
break;
case ')':
depth--;
break;
case ',':
if (depth == 0) {
std::string s = str.substr(last, i - last);
components.add(s);
last = i + 1;
}
break;
default:
break;
}
}
std::string s = str.substr(last);
if (s.length() != 0) {
components.add(s);
}
return components;
}
////////////////////////////////////////////////////////////////////////////////
std::string URISupport::stripPrefix(const std::string& value, const std::string& prefix) {
if (value.find(prefix) == 0) {
return value.substr(prefix.length());
}
return value;
}
////////////////////////////////////////////////////////////////////////////////
URI URISupport::stripScheme(const URI& uri) {
return URI(stripPrefix(uri.getSchemeSpecificPart(), "//"));
}
////////////////////////////////////////////////////////////////////////////////
Properties URISupport::parseParameters(const URI& uri) {
if (!isCompositeURI(uri)) {
return uri.getQuery().empty() ? Properties() : parseQuery(stripPrefix(uri.getQuery(), "?"));
} else {
return URISupport::parseComposite(uri).getParameters();
}
}
////////////////////////////////////////////////////////////////////////////////
URI URISupport::applyParameters(const URI& uri, const Properties& queryParameters) {
return applyParameters(uri, queryParameters, "");
}
////////////////////////////////////////////////////////////////////////////////
URI URISupport::applyParameters(const URI& uri, const Properties& queryParameters, const std::string& optionPrefix) {
URI result;
if (!queryParameters.isEmpty()) {
std::string newQuery = uri.getRawQuery();
std::vector<std::string> keys = queryParameters.propertyNames();
std::vector<std::string>::iterator iter = keys.begin();
for (; iter != keys.end(); ++iter) {
std::string option = *iter;
if (option.find(optionPrefix) == 0) {
if (newQuery.length() != 0) {
newQuery.append("&");
}
std::string newKey = option;
if (!optionPrefix.empty()) {
newKey = option.substr(optionPrefix.length());
}
newQuery.append(newKey).append("=").append(queryParameters.getProperty(option));
}
}
result = createURIWithQuery(uri, newQuery);
}
return result;
}
////////////////////////////////////////////////////////////////////////////////
URI URISupport::createURIWithQuery(const URI& uri, const std::string& query) {
std::string schemeSpecificPart = uri.getRawSchemeSpecificPart();
// strip existing query if any
std::size_t questionMark = schemeSpecificPart.find_last_of("?");
// make sure question mark is not within parentheses
std::size_t lastParend = schemeSpecificPart.find_last_of(")");
if (lastParend != std::string::npos && questionMark < lastParend) {
questionMark = std::string::npos;
}
if (questionMark != std::string::npos) {
schemeSpecificPart = schemeSpecificPart.substr(0, questionMark);
}
if (!query.empty()) {
schemeSpecificPart += "?" + query;
}
return URI(uri.getScheme(), schemeSpecificPart, uri.getFragment());
}