Commit 265400d6 authored by Kenton Varda's avatar Kenton Varda Committed by GitHub

Merge pull request #492 from sandstorm-io/tweaks

Tweaks and bugfixes
parents e92255a0 3e0aae88
...@@ -378,5 +378,12 @@ TEST(Array, ReleaseAsBytesOrChars) { ...@@ -378,5 +378,12 @@ TEST(Array, ReleaseAsBytesOrChars) {
} }
} }
#if __cplusplus > 201402L
KJ_TEST("kj::arr()") {
kj::Array<kj::String> array = kj::arr(kj::str("foo"), kj::str(123));
KJ_EXPECT(array == kj::ArrayPtr<const kj::StringPtr>({"foo", "123"}));
}
#endif
} // namespace } // namespace
} // namespace kj } // namespace kj
...@@ -177,6 +177,11 @@ public: ...@@ -177,6 +177,11 @@ public:
inline T& front() { return *ptr; } inline T& front() { return *ptr; }
inline T& back() { return *(ptr + size_ - 1); } inline T& back() { return *(ptr + size_ - 1); }
template <typename U>
inline bool operator==(const U& other) const { return asPtr() == other; }
template <typename U>
inline bool operator!=(const U& other) const { return asPtr() != other; }
inline ArrayPtr<T> slice(size_t start, size_t end) { inline ArrayPtr<T> slice(size_t start, size_t end) {
KJ_IREQUIRE(start <= end && end <= size_, "Out-of-bounds Array::slice()."); KJ_IREQUIRE(start <= end && end <= size_, "Out-of-bounds Array::slice().");
return ArrayPtr<T>(ptr + start, end - start); return ArrayPtr<T>(ptr + start, end - start);
...@@ -249,6 +254,8 @@ private: ...@@ -249,6 +254,8 @@ private:
template <typename U> template <typename U>
friend class Array; friend class Array;
template <typename U>
friend class ArrayBuilder;
}; };
static_assert(!canMemcpy<Array<char>>(), "canMemcpy<>() is broken"); static_assert(!canMemcpy<Array<char>>(), "canMemcpy<>() is broken");
...@@ -321,6 +328,13 @@ public: ...@@ -321,6 +328,13 @@ public:
other.pos = nullptr; other.pos = nullptr;
other.endPtr = nullptr; other.endPtr = nullptr;
} }
ArrayBuilder(Array<T>&& other)
: ptr(other.ptr), pos(other.ptr + other.size_), endPtr(pos), disposer(other.disposer) {
// Create an already-full ArrayBuilder from an Array of the same type. This constructor
// primarily exists to enable Vector<T> to be constructed from Array<T>.
other.ptr = nullptr;
other.size_ = 0;
}
KJ_DISALLOW_COPY(ArrayBuilder); KJ_DISALLOW_COPY(ArrayBuilder);
inline ~ArrayBuilder() noexcept(false) { dispose(); } inline ~ArrayBuilder() noexcept(false) { dispose(); }
...@@ -808,6 +822,15 @@ inline Array<T> heapArray(std::initializer_list<T> init) { ...@@ -808,6 +822,15 @@ inline Array<T> heapArray(std::initializer_list<T> init) {
return heapArray<T>(init.begin(), init.end()); return heapArray<T>(init.begin(), init.end());
} }
#if __cplusplus > 201402L
template <typename T, typename... Params>
inline Array<Decay<T>> arr(T&& param1, Params&&... params) {
ArrayBuilder<Decay<T>> builder = heapArrayBuilder<Decay<T>>(sizeof...(params) + 1);
(builder.add(kj::fwd<T>(param1)), ... , builder.add(kj::fwd<Params>(params)));
return builder.finish();
}
#endif
} // namespace kj } // namespace kj
#endif // KJ_ARRAY_H_ #endif // KJ_ARRAY_H_
...@@ -86,8 +86,9 @@ class AsyncOutputStream { ...@@ -86,8 +86,9 @@ class AsyncOutputStream {
// Asynchronous equivalent of OutputStream (from io.h). // Asynchronous equivalent of OutputStream (from io.h).
public: public:
virtual Promise<void> write(const void* buffer, size_t size) = 0; virtual Promise<void> write(const void* buffer, size_t size) KJ_WARN_UNUSED_RESULT = 0;
virtual Promise<void> write(ArrayPtr<const ArrayPtr<const byte>> pieces) = 0; virtual Promise<void> write(ArrayPtr<const ArrayPtr<const byte>> pieces)
KJ_WARN_UNUSED_RESULT = 0;
virtual Maybe<Promise<uint64_t>> tryPumpFrom( virtual Maybe<Promise<uint64_t>> tryPumpFrom(
AsyncInputStream& input, uint64_t amount = kj::maxValue); AsyncInputStream& input, uint64_t amount = kj::maxValue);
......
...@@ -1297,7 +1297,7 @@ public: ...@@ -1297,7 +1297,7 @@ public:
KJ_REQUIRE(inBody) { return kj::READY_NOW; } KJ_REQUIRE(inBody) { return kj::READY_NOW; }
auto fork = writeQueue.then([this,buffer,size]() { auto fork = writeQueue.then([this,buffer,size]() {
inner.write(buffer, size); return inner.write(buffer, size);
}).fork(); }).fork();
writeQueue = fork.addBranch(); writeQueue = fork.addBranch();
...@@ -1308,7 +1308,7 @@ public: ...@@ -1308,7 +1308,7 @@ public:
KJ_REQUIRE(inBody) { return kj::READY_NOW; } KJ_REQUIRE(inBody) { return kj::READY_NOW; }
auto fork = writeQueue.then([this,pieces]() { auto fork = writeQueue.then([this,pieces]() {
inner.write(pieces); return inner.write(pieces);
}).fork(); }).fork();
writeQueue = fork.addBranch(); writeQueue = fork.addBranch();
......
...@@ -171,42 +171,36 @@ Maybe<Url> Url::tryParse(StringPtr text, Context context) { ...@@ -171,42 +171,36 @@ Maybe<Url> Url::tryParse(StringPtr text, Context context) {
} }
} }
{ while (text.startsWith("/")) {
Vector<String> path; text = text.slice(1);
while (text.startsWith("/")) { auto part = split(text, END_PATH_PART);
text = text.slice(1); if (part.size() == 2 && part[0] == '.' && part[1] == '.') {
auto part = split(text, END_PATH_PART); if (result.path.size() != 0) {
if (part.size() == 2 && part[0] == '.' && part[1] == '.') { result.path.removeLast();
if (path.size() != 0) {
path.removeLast();
}
result.hasTrailingSlash = true;
} else if (part.size() == 0 || (part.size() == 1 && part[0] == '.')) {
// Collapse consecutive slashes and "/./".
result.hasTrailingSlash = true;
} else {
path.add(percentDecode(part, err));
result.hasTrailingSlash = false;
} }
result.hasTrailingSlash = true;
} else if (part.size() == 0 || (part.size() == 1 && part[0] == '.')) {
// Collapse consecutive slashes and "/./".
result.hasTrailingSlash = true;
} else {
result.path.add(percentDecode(part, err));
result.hasTrailingSlash = false;
} }
result.path = path.releaseAsArray();
} }
if (text.startsWith("?")) { if (text.startsWith("?")) {
Vector<QueryParam> params;
do { do {
text = text.slice(1); text = text.slice(1);
auto part = split(text, END_QUERY_PART); auto part = split(text, END_QUERY_PART);
if (part.size() > 0) { if (part.size() > 0) {
KJ_IF_MAYBE(key, trySplit(part, '=')) { KJ_IF_MAYBE(key, trySplit(part, '=')) {
params.add(QueryParam { percentDecode(*key, err), percentDecode(part, err) }); result.query.add(QueryParam { percentDecode(*key, err), percentDecode(part, err) });
} else { } else {
params.add(QueryParam { percentDecode(part, err), nullptr }); result.query.add(QueryParam { percentDecode(part, err), nullptr });
} }
} }
} while (text.startsWith("&")); } while (text.startsWith("&"));
result.query = params.releaseAsArray();
} }
if (text.startsWith("#")) { if (text.startsWith("#")) {
...@@ -293,7 +287,6 @@ Maybe<Url> Url::tryParseRelative(StringPtr text) const { ...@@ -293,7 +287,6 @@ Maybe<Url> Url::tryParseRelative(StringPtr text) const {
bool hadNewPath = text.size() > 0 && text[0] != '?' && text[0] != '#'; bool hadNewPath = text.size() > 0 && text[0] != '?' && text[0] != '#';
if (hadNewPath) { if (hadNewPath) {
// There's a new path. // There's a new path.
Vector<String> path(this->path.size());
if (text[0] == '/') { if (text[0] == '/') {
// New path is absolute, so don't copy the old path. // New path is absolute, so don't copy the old path.
...@@ -303,9 +296,7 @@ Maybe<Url> Url::tryParseRelative(StringPtr text) const { ...@@ -303,9 +296,7 @@ Maybe<Url> Url::tryParseRelative(StringPtr text) const {
// New path is relative, so start from the old path, dropping everything after the last // New path is relative, so start from the old path, dropping everything after the last
// slash. // slash.
auto slice = this->path.slice(0, this->path.size() - (this->hasTrailingSlash ? 0 : 1)); auto slice = this->path.slice(0, this->path.size() - (this->hasTrailingSlash ? 0 : 1));
for (auto& part: slice) { result.path = KJ_MAP(part, slice) { return kj::str(part); };
path.add(kj::str(part));
}
result.hasTrailingSlash = true; result.hasTrailingSlash = true;
} }
...@@ -313,22 +304,20 @@ Maybe<Url> Url::tryParseRelative(StringPtr text) const { ...@@ -313,22 +304,20 @@ Maybe<Url> Url::tryParseRelative(StringPtr text) const {
auto part = split(text, END_PATH_PART); auto part = split(text, END_PATH_PART);
if (part.size() == 2 && part[0] == '.' && part[1] == '.') { if (part.size() == 2 && part[0] == '.' && part[1] == '.') {
if (path.size() != 0) { if (path.size() != 0) {
path.removeLast(); result.path.removeLast();
} }
result.hasTrailingSlash = true; result.hasTrailingSlash = true;
} else if (part.size() == 0 || (part.size() == 1 && part[0] == '.')) { } else if (part.size() == 0 || (part.size() == 1 && part[0] == '.')) {
// Collapse consecutive slashes and "/./". // Collapse consecutive slashes and "/./".
result.hasTrailingSlash = true; result.hasTrailingSlash = true;
} else { } else {
path.add(percentDecode(part, err)); result.path.add(percentDecode(part, err));
result.hasTrailingSlash = false; result.hasTrailingSlash = false;
} }
if (!text.startsWith("/")) break; if (!text.startsWith("/")) break;
text = text.slice(1); text = text.slice(1);
} }
result.path = path.releaseAsArray();
} else if (!hadNewAuthority) { } else if (!hadNewAuthority) {
// copy path // copy path
result.path = KJ_MAP(part, this->path) { return kj::str(part); }; result.path = KJ_MAP(part, this->path) { return kj::str(part); };
...@@ -336,20 +325,18 @@ Maybe<Url> Url::tryParseRelative(StringPtr text) const { ...@@ -336,20 +325,18 @@ Maybe<Url> Url::tryParseRelative(StringPtr text) const {
} }
if (text.startsWith("?")) { if (text.startsWith("?")) {
Vector<QueryParam> params;
do { do {
text = text.slice(1); text = text.slice(1);
auto part = split(text, END_QUERY_PART); auto part = split(text, END_QUERY_PART);
if (part.size() > 0) { if (part.size() > 0) {
KJ_IF_MAYBE(key, trySplit(part, '=')) { KJ_IF_MAYBE(key, trySplit(part, '=')) {
params.add(QueryParam { percentDecode(*key, err), percentDecode(part, err) }); result.query.add(QueryParam { percentDecode(*key, err), percentDecode(part, err) });
} else { } else {
params.add(QueryParam { percentDecode(part, err), nullptr }); result.query.add(QueryParam { percentDecode(part, err), nullptr });
} }
} }
} while (text.startsWith("&")); } while (text.startsWith("&"));
result.query = params.releaseAsArray();
} else if (!hadNewAuthority && !hadNewPath) { } else if (!hadNewAuthority && !hadNewPath) {
// copy query // copy query
result.query = KJ_MAP(param, this->query) { result.query = KJ_MAP(param, this->query) {
...@@ -404,6 +391,11 @@ String Url::toString(Context context) const { ...@@ -404,6 +391,11 @@ String Url::toString(Context context) const {
} }
for (auto& pathPart: path) { for (auto& pathPart: path) {
// Protect against path injection.
KJ_REQUIRE(pathPart != "" && pathPart != "." && pathPart != "..",
"invalid name in URL path", *this) {
continue;
}
chars.add('/'); chars.add('/');
chars.addAll(encodeUriComponent(pathPart)); chars.addAll(encodeUriComponent(pathPart));
} }
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#define KJ_COMPAT_URL_H_ #define KJ_COMPAT_URL_H_
#include <kj/string.h> #include <kj/string.h>
#include <kj/vector.h>
#include <inttypes.h> #include <inttypes.h>
namespace kj { namespace kj {
...@@ -48,16 +49,20 @@ struct Url { ...@@ -48,16 +49,20 @@ struct Url {
// network address parsing functions already accept addresses containing port numbers, and // network address parsing functions already accept addresses containing port numbers, and
// because most web standards don't actually want to separate host and port. // because most web standards don't actually want to separate host and port.
Array<String> path; Vector<String> path;
bool hasTrailingSlash = false; bool hasTrailingSlash = false;
// Path, split on '/' characters. Note that the individual components of `path` could contain // Path, split on '/' characters. Note that the individual components of `path` could contain
// '/' characters if they were percent-encoded in the original URL. // '/' characters if they were percent-encoded in the original URL.
//
// No component of the path is allowed to be "", ".", nor ".."; if such components are present,
// toString() will throw. Note that parse() and parseRelative() automatically resolve such
// components.
struct QueryParam { struct QueryParam {
String name; String name;
String value; String value;
}; };
Array<QueryParam> query; Vector<QueryParam> query;
// Query, e.g. from "?key=value&key2=value2". If a component of the query contains no '=' sign, // Query, e.g. from "?key=value&key2=value2". If a component of the query contains no '=' sign,
// it will be parsed as a key with an empty value. // it will be parsed as a key with an empty value.
...@@ -71,12 +76,14 @@ struct Url { ...@@ -71,12 +76,14 @@ struct Url {
~Url() noexcept(false); ~Url() noexcept(false);
Url& operator=(Url&&) = default; Url& operator=(Url&&) = default;
inline Url(String&& scheme, Maybe<UserInfo>&& userInfo, String&& host, Array<String>&& path, #if __cplusplus < 201402L
bool hasTrailingSlash, Array<QueryParam>&& query, Maybe<String>&& fragment) inline Url(String&& scheme, Maybe<UserInfo>&& userInfo, String&& host, Vector<String>&& path,
bool hasTrailingSlash, Vector<QueryParam>&& query, Maybe<String>&& fragment)
: scheme(kj::mv(scheme)), userInfo(kj::mv(userInfo)), host(kj::mv(host)), path(kj::mv(path)), : scheme(kj::mv(scheme)), userInfo(kj::mv(userInfo)), host(kj::mv(host)), path(kj::mv(path)),
hasTrailingSlash(hasTrailingSlash), query(kj::mv(query)), fragment(kj::mv(fragment)) {} hasTrailingSlash(hasTrailingSlash), query(kj::mv(query)), fragment(kj::mv(fragment)) {}
// TODO(cleanup): This constructor is only here to support brace initialization in C++11. It // TODO(cleanup): This constructor is only here to support brace initialization in C++11. It
// should be removed once we upgrade to C++14. // should be removed once we upgrade to C++14.
#endif
Url clone() const; Url clone() const;
......
...@@ -43,6 +43,7 @@ class Vector { ...@@ -43,6 +43,7 @@ class Vector {
public: public:
inline Vector() = default; inline Vector() = default;
inline explicit Vector(size_t capacity): builder(heapArrayBuilder<T>(capacity)) {} inline explicit Vector(size_t capacity): builder(heapArrayBuilder<T>(capacity)) {}
inline Vector(Array<T>&& array): builder(kj::mv(array)) {}
inline operator ArrayPtr<T>() { return builder; } inline operator ArrayPtr<T>() { return builder; }
inline operator ArrayPtr<const T>() const { return builder; } inline operator ArrayPtr<const T>() const { return builder; }
...@@ -71,6 +72,18 @@ public: ...@@ -71,6 +72,18 @@ public:
return builder.finish(); return builder.finish();
} }
template <typename U>
inline bool operator==(const U& other) const { return asPtr() == other; }
template <typename U>
inline bool operator!=(const U& other) const { return asPtr() != other; }
inline ArrayPtr<T> slice(size_t start, size_t end) {
return asPtr().slice(start, end);
}
inline ArrayPtr<const T> slice(size_t start, size_t end) const {
return asPtr().slice(start, end);
}
template <typename... Params> template <typename... Params>
inline T& add(Params&&... params) { inline T& add(Params&&... params) {
if (builder.isFull()) grow(); if (builder.isFull()) grow();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment