From a1445d9459458f0837ad90c3f21e6905ead2069e Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 28 Sep 2020 12:15:01 +0200 Subject: [PATCH] Add QueryParser.h for decoding URI queries by key --- src/HttpParser.h | 10 ++++ src/QueryParser.h | 113 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 src/QueryParser.h diff --git a/src/HttpParser.h b/src/HttpParser.h index 7530417..022e65a 100644 --- a/src/HttpParser.h +++ b/src/HttpParser.h @@ -29,6 +29,7 @@ #include "BloomFilter.h" #include "ProxyParser.h" +#include "QueryParser.h" namespace uWS { @@ -108,6 +109,7 @@ public: return std::string_view(headers->key.data(), headers->key.length()); } + /* Returns the raw querystring as a whole, still encoded */ std::string_view getQuery() { if (querySeparator < (int) headers->value.length()) { /* Strip the initial ? */ @@ -117,6 +119,14 @@ public: } } + /* Finds and decodes the URI component. */ + std::string_view getQuery(std::string_view key) { + /* Raw querystring including initial '?' sign */ + std::string_view queryString = std::string_view(headers->value.data() + querySeparator, headers->value.length() - querySeparator); + + return getDecodedQueryValue(key, queryString); + } + void setParameters(std::pair parameters) { currentParameters = parameters; } diff --git a/src/QueryParser.h b/src/QueryParser.h new file mode 100644 index 0000000..9dd3b10 --- /dev/null +++ b/src/QueryParser.h @@ -0,0 +1,113 @@ +/* + * Authored by Alex Hultman, 2018-2020. + * Intellectual property of third-party. + + * Licensed 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. + */ + +/* This module implements URI query parsing and retrieval of value given key */ + +#include + +namespace uWS { + + /* Takes raw query including initial '?' sign. Will inplace decode, so input will mutate */ + std::string_view getDecodedQueryValue(std::string_view key, std::string_view rawQuery) { + + /* Can't have a value without a key */ + if (!key.length()) { + return {}; + } + + /* Start with the whole querystring including initial '?' */ + std::string_view queryString = rawQuery; + + /* List of key, value could be cached for repeated fetches similar to how headers are, todo! */ + while (queryString.length()) { + /* Find boundaries of this statement */ + std::string_view statement = queryString.substr(1, queryString.find('&', 1) - 1); + + /* Only bother if first char of key match (early exit) */ + if (statement.length() && statement[0] == key[0]) { + /* Equal sign must be present and not in the end of statement */ + auto equality = statement.find('='); + if (equality != std::string_view::npos && equality != statement.length() - 1) { + + std::string_view statementKey = statement.substr(0, equality); + std::string_view statementValue = statement.substr(equality + 1); + + /* String comparison */ + if (key == statementKey) { + + /* Decode value inplace, put null at end if before length of original */ + char *in = (char *) statementValue.data(); + + /* Write offset */ + int out = 0; + + /* Walk over all chars until end or null char, decoding in place */ + for (int i = 0; in[i] && i < statementValue.length(); i++) { + /* Only bother with '%' */ + if (in[i] == '%') { + /* Do we have enough data for two bytes hex? */ + if (i + 2 >= statementValue.length()) { + return {}; + } + + /* Two bytes hex */ + char hex1 = in[i + 1] - '0'; + if (hex1 > 9) { + hex1 -= 7; + } + + char hex2 = in[i + 2] - '0'; + if (hex2 > 9) { + hex2 -= 7; + } + + in[out] = hex1 * 16 + hex2; + i += 2; + } else { + /* Is this even a rule? */ + if (in[i] == '+') { + in[out] = ' '; + } else { + in[out] = in[i]; + } + } + + /* We always only write one char */ + out++; + } + + /* If decoded string is shorter than original, put null char to stop next read */ + if (out < statementValue.length()) { + in[out] = 0; + } + + return statementValue.substr(0, out); + } + } else { + /* This querystring is invalid, cannot parse it */ + return {}; + } + } + + queryString.remove_prefix(statement.length() + 1); + } + + /* Nothing found */ + return {}; + } + +} \ No newline at end of file