#include "ResourceResolver.hpp" namespace httpsserver { ResourceResolver::ResourceResolver() { _nodes = new std::vector(); _defaultNode = NULL; } ResourceResolver::~ResourceResolver() { delete _nodes; } /** * This method will register the HTTPSNode so it is reachable and its callback gets called for a request */ void ResourceResolver::registerNode(HTTPNode *node) { _nodes->push_back(node); } /** * This method can be used to deactivate a HTTPSNode that has been registered previously */ void ResourceResolver::unregisterNode(HTTPNode *node) { } void ResourceResolver::resolveNode(const std::string &method, const std::string &url, ResolvedResource &resolvedResource, HTTPNodeType nodeType) { // Reset the resource resolvedResource.setMatchingNode(NULL); resolvedResource.setParams(NULL); // Memory management of this object will be performed by the ResolvedResource instance ResourceParameters * params = new ResourceParameters(); // Split URL in resource name and request params. Request params start after an optional '?' size_t reqparamIdx = url.find('?'); // Store this index to stop path parsing there size_t pathEnd = reqparamIdx != std::string::npos ? reqparamIdx : url.size(); // If no '?' is contained in url, 0:npos will return the string as it is std::string resourceName = url.substr(0, reqparamIdx); // Set request params in params object if a '?' exists if (reqparamIdx != std::string::npos) { do { // Drop the '?' or '&' reqparamIdx += 1; // Parameters are separated by '&' size_t nextparamIdx = url.find('&', reqparamIdx); // Get the "name=value" string std::string param = nextparamIdx == std::string::npos ? url.substr(reqparamIdx) : url.substr(reqparamIdx, nextparamIdx - reqparamIdx); if (param.length() > 0) { // Find the position where the string has to be split size_t nvSplitIdx = param.find('='); // Use empty string if only name is set. /foo?bar&baz=1 will return "" for bar std::string name = urlDecode(param.substr(0, nvSplitIdx)); std::string value = ""; if (nvSplitIdx != std::string::npos) { value = urlDecode(param.substr(nvSplitIdx+1)); } // Now we finally have name and value. params->setQueryParameter(name, value); } // Update reqparamIdx reqparamIdx = nextparamIdx; } while(reqparamIdx != std::string::npos); } // Check whether a resource matches for(std::vector::iterator itNode = _nodes->begin(); itNode != _nodes->end(); ++itNode) { params->resetPathParameters(); HTTPNode *node = *itNode; if (node->_nodeType==nodeType) { if ( // For handler functions, check the method declared with the node (node->_nodeType==HANDLER_CALLBACK && ((ResourceNode*)node)->_method == method) || // For websockets, the specification says that GET is the only choice (node->_nodeType==WEBSOCKET && method=="GET") ) { HTTPS_LOGD("Testing route %s", node->_path.c_str()); bool match = true; size_t paramCount = node->getPathParamCount(); // indices in input and pattern size_t inputIdx = 0, pathIdx = 0; HTTPS_LOGD("(INIT) inputIdx: %d, pathIdx: %d, pathEnd: %d, path: %s, url: %s", inputIdx, pathIdx, pathEnd, node->_path.c_str(), url.c_str()); for (size_t paramIdx = 0; match && paramIdx < paramCount; paramIdx += 1) { HTTPS_LOGD("(LOOP) inputIdx: %d, pathIdx: %d, pathEnd: %d, path: %s, url: %s", inputIdx, pathIdx, pathEnd, node->_path.c_str(), url.c_str()); // Test static path before the parameter size_t paramPos = node->getParamIdx(paramIdx); size_t staticLength = paramPos - pathIdx; match &= url.substr(inputIdx, staticLength) == node->_path.substr(pathIdx, staticLength); inputIdx += staticLength; pathIdx += staticLength; // Extract parameter value if (match) { size_t paramEnd = url.find('/', inputIdx); if (paramEnd == std::string::npos && inputIdx <= pathEnd) { // Consume the remaining input (might be "" for the last param) paramEnd = pathEnd; } if (paramEnd != std::string::npos) { size_t paramLength = paramEnd - inputIdx; params->setPathParameter(paramIdx, urlDecode(url.substr(inputIdx, paramLength))); pathIdx += 1; inputIdx += paramLength; } else { match = false; HTTPS_LOGD("(LOOP) No match on param part"); } } else { HTTPS_LOGD("(LOOP) No match on static part"); } } HTTPS_LOGD("(STTC) inputIdx: %d, pathIdx: %d, pathEnd: %d, path: %s, url: %s", inputIdx, pathIdx, pathEnd, node->_path.c_str(), url.c_str()); // Test static path after the parameter (up to pathEnd) if (match) { match = url.substr(inputIdx, pathEnd - inputIdx)==node->_path.substr(pathIdx); } HTTPS_LOGD("(END ) inputIdx: %d, pathIdx: %d, pathEnd: %d, path: %s, url: %s", inputIdx, pathIdx, pathEnd, node->_path.c_str(), url.c_str()); if (match) { resolvedResource.setMatchingNode(node); HTTPS_LOGD("It's a match!"); break; } } // method check } // node type check } // resource node for loop // If the resource did not match, configure the default resource if (!resolvedResource.didMatch() && _defaultNode != NULL) { params->resetPathParameters(); resolvedResource.setMatchingNode(_defaultNode); } // If resolving did work, set the params, otherwise delete them if (resolvedResource.didMatch()) { // The resolvedResource now takes care of memory management for the params resolvedResource.setParams(params); } else { delete params; } } void ResourceResolver::addMiddleware(const HTTPSMiddlewareFunction * mwFunction) { _middleware.push_back(mwFunction); } void ResourceResolver::removeMiddleware(const HTTPSMiddlewareFunction * mwFunction) { _middleware.erase(std::remove(_middleware.begin(), _middleware.end(), mwFunction), _middleware.end()); } const std::vector ResourceResolver::getMiddleware() { return _middleware; } void ResourceResolver::setDefaultNode(HTTPNode * defaultNode) { _defaultNode = defaultNode; } }