| /* |
| * 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()); |
| } |