Skip to content

Commit

Permalink
refs #4332 implemented a parse_url() variant that allows for percent …
Browse files Browse the repository at this point in the history
…decoding string URL components
  • Loading branch information
davidnich committed Oct 16, 2021
1 parent a636b2e commit f3d5fb4
Show file tree
Hide file tree
Showing 6 changed files with 517 additions and 157 deletions.
4 changes: 3 additions & 1 deletion doxygen/lang/900_release_notes.dox.tmpl
Expand Up @@ -8,7 +8,9 @@
Bugfix release; see below for more information

@subsection qore_1_0_10_bug_fixes Bug Fixes in Qore
- fixed C++ foreign thread registration APIs to allow for TID reuse to provide thread affinity for language
- implemented the @ref Qore::parse_url(string, int) variant to allow for percent decoding in URL strings
(<a href="https://github.com/qorelanguage/qore/issues/4332">issue 4332</a>)
- fixed C++ foreign thread registration APIs to support TID reuse to provide thread affinity for language
modules such as Python and Java with successive rapid transitions into %Qore in callbacks
(<a href="https://github.com/qorelanguage/qore/issues/4331">issue 4331</a>)

Expand Down
17 changes: 17 additions & 0 deletions examples/test/qore/functions/parse_url.qtest
Expand Up @@ -12,6 +12,7 @@

public class parseUrlTest inherits QUnit::Test {
constructor() : Test("parse_url test", "1.0") {
addTestCase("decode test", \decodeTest());
addTestCase("slash test", \slashTest());
addTestCase("Test 1", \test());
addTestCase("port test", \portTest());
Expand All @@ -20,6 +21,22 @@ public class parseUrlTest inherits QUnit::Test {
set_return_value(main());
}

decodeTest() {
hash<UrlInfo> h = parse_url("https://user-%3cname%3e%40example.com%2f"
"token:pass%3f%3aword@qoretechnologies.zendesk.com/api/v2/users%40str", QURL_DECODE);
assertEq("user-<name>@example.com/token", h.username);
assertEq("pass?:word", h.password);
assertEq("qoretechnologies.zendesk.com", h.host);
assertEq("/api/v2/users%40str", h.path);

h = parse_url("https://user-%3cname%3e%40example.com%2f"
"token:pass%3f%3aword@qoretechnologies.zendesk.com/api/v2/users%40str", QURL_DECODE_PATH);
assertEq("user-<name>@example.com/token", h.username);
assertEq("pass?:word", h.password);
assertEq("qoretechnologies.zendesk.com", h.host);
assertEq("/api/v2/users@str", h.path);
}

slashTest() {
hash<auto> h = parse_url("https://user@example.com/token:api-token@qoretechnologies.zendesk.com/api/v2/users");
assertEq("user@example.com/token", h.username);
Expand Down
207 changes: 178 additions & 29 deletions include/qore/QoreURL.h
Expand Up @@ -6,7 +6,7 @@
Qore Programming Language
Copyright (C) 2003 - 2019 Qore Technologies, s.r.o.
Copyright (C) 2003 - 2021 Qore Technologies, s.r.o.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
Expand Down Expand Up @@ -35,22 +35,13 @@

#define _QORE_QOREURL_H

#define QURL_KEEP_BRACKETS (1 << 0)
#define QURL_DECODE (1 << 1)
#define QURL_DECODE_PATH (1 << 2)
#define QURL_DECODE_ANY (QURL_DECODE | QURL_DECODE_PATH)

//! helps with parsing URLs and provides access to URL components through Qore data structures
class QoreURL {
private:
//! private implementation of the class
struct qore_url_private* priv;

DLLLOCAL void zero();
DLLLOCAL void reset();
DLLLOCAL void parseIntern(const char* url, ExceptionSink* xsink);

//! this function is not implemented; it is here as a private function in order to prohibit it from being used
DLLLOCAL QoreURL(const QoreURL&);

//! this function is not implemented; it is here as a private function in order to prohibit it from being used
DLLLOCAL QoreURL& operator=(const QoreURL&);

public:
//! creates an empty structure
/** @see QoreURL::parse()
Expand All @@ -59,27 +50,29 @@ class QoreURL {

//! parses the URL string passed
/** you can check if the URL was valid by calling QoreURL::isValid() after this call
@param url the URL string to parse
@param url the URL string to parse
*/
DLLEXPORT QoreURL(const char* url);

//! parses the URL string passed
/** you can check if the URL was valid by calling QoreURL::isValid() after this call
@param url the URL string to parse
@param url the URL string to parse
*/
DLLEXPORT QoreURL(const QoreString* url);

//! parses the URL string passed
/** you can check if the URL was valid by calling QoreURL::isValid() after this call
@param url the URL string to parse
@param keep_brackets if this argument is true then if the hostname or address is enclosed in square brackets, then the brackets will be included in the \c "host" key output as well
@param url the URL string to parse
@param keep_brackets if this argument is true then if the hostname or address is enclosed in square brackets,
then the brackets will be included in the \c "host" key output as well
*/
DLLEXPORT QoreURL(const char* url, bool keep_brackets);

//! parses the URL string passed
/** you can check if the URL was valid by calling QoreURL::isValid() after this call
@param url the URL string to parse
@param keep_brackets if this argument is true then if the hostname or address is enclosed in square brackets, then the brackets will be included in the \c "host" key output as well
@param url the URL string to parse
@param keep_brackets if this argument is true then if the hostname or address is enclosed in square brackets,
then the brackets will be included in the \c "host" key output as well
*/
DLLEXPORT QoreURL(const QoreString* url, bool keep_brackets);

Expand All @@ -96,51 +89,118 @@ class QoreURL {
*/
DLLEXPORT QoreURL(const QoreString* url, bool keep_brackets, ExceptionSink* xsink);

//! parses the URL string passed
/** you can check if the URL was valid by calling QoreURL::isValid() after this call
@param url the URL string to parse
@param options a bitfield of %Qore URL options
@since %Qore 1.0.10
*/
DLLEXPORT QoreURL(const char* url, int options);

//! parses the URL string passed
/** you can check if the URL was valid by calling QoreURL::isValid() after this call
@param url the URL string to parse
@param options a bitfield of %Qore URL options
@since %Qore 1.0.10
*/
DLLEXPORT QoreURL(const QoreString& url, int options = 0);

//! parses the URL string passed
/** you can check if the URL was valid by calling QoreURL::isValid() after this call
@param url the URL string to parse
@param options a bitfield of %Qore URL options
@since %Qore 1.0.10
*/
DLLEXPORT QoreURL(const std::string& url, int options = 0);

//! parses the URL string passed
/** you can check if the URL was valid by calling QoreURL::isValid() after this call
@param xsink for Qore-language exceptions
@param url the URL string to parse
@param options a bitfield of %Qore URL options
@since %Qore 1.0.10
*/
DLLEXPORT QoreURL(ExceptionSink* xsink, const char* url, int options);

//! parses the URL string passed
/** you can check if the URL was valid by calling QoreURL::isValid() after this call
@param xsink for Qore-language exceptions
@param url the URL string to parse
@param options a bitfield of %Qore URL options
@since %Qore 1.0.10
*/
DLLEXPORT QoreURL(ExceptionSink* xsink, const QoreString& url, int options = 0);

//! parses the URL string passed
/** you can check if the URL was valid by calling QoreURL::isValid() after this call
@param xsink for Qore-language exceptions
@param url the URL string to parse
@param options a bitfield of %Qore URL options
@since %Qore 1.0.10
*/
DLLEXPORT QoreURL(ExceptionSink* xsink, const std::string& url, int options = 0);

//! frees all memory and destroys the structure
DLLEXPORT ~QoreURL();

//! parses the URL string passed
/** If a url was already parsed previously, all memory is freed before parsing the new string.
You can check if the URL was valid by calling QoreURL::isValid() after this call
You can check if the URL was valid by calling QoreURL::isValid() after this call
@param url the URL string to parse
*/
DLLEXPORT int parse(const char* url);

//! parses the URL string passed
/** If a url was already parsed previously, all memory is freed before parsing the new string.
You can check if the URL was valid by calling QoreURL::isValid() after this call
You can check if the URL was valid by calling QoreURL::isValid() after this call
@param url the URL string to parse
*/
DLLEXPORT int parse(const QoreString* url);

//! parses the URL string passed
/** If a url was already parsed previously, all memory is freed before parsing the new string.
You can check if the URL was valid by calling QoreURL::isValid() after this call
You can check if the URL was valid by calling QoreURL::isValid() after this call
@param url the URL string to parse
@param keep_brackets if this argument is true then if the hostname or address is enclosed in square brackets, then the brackets will be included in the \c "host" key output as well
@param keep_brackets if this argument is true then if the hostname or address is enclosed in square brackets,
then the brackets will be included in the \c "host" key output as well
@return 0 if the URL was parsed successfully, -1 if not
*/
DLLEXPORT int parse(const char* url, bool keep_brackets);

//! parses the URL string passed
/** If a url was already parsed previously, all memory is freed before parsing the new string.
You can check if the URL was valid by calling QoreURL::isValid() after this call
You can check if the URL was valid by calling QoreURL::isValid() after this call
@param url the URL string to parse
@param keep_brackets if this argument is true then if the hostname or address is enclosed in square brackets, then the brackets will be included in the \c "host" key output as well
@param keep_brackets if this argument is true then if the hostname or address is enclosed in square brackets,
then the brackets will be included in the \c "host" key output as well
@return 0 if the URL was parsed successfully, -1 if not
*/
DLLEXPORT int parse(const QoreString* url, bool keep_brackets);

//! parses the URL string passed
/** If a url was already parsed previously, all memory is freed before parsing the new string.
You can check if the URL was valid by calling QoreURL::isValid() after this call
You can check if the URL was valid by calling QoreURL::isValid() after this call
@param url the URL string to parse
@param keep_brackets if this argument is true then if the hostname or address is enclosed in square brackets, then the brackets will be included in the \c "host" key output as well
@param keep_brackets if this argument is true then if the hostname or address is enclosed in square brackets,
then the brackets will be included in the \c "host" key output as well
@param xsink for Qore-language exceptions
@return 0 if the URL was parsed successfully, -1 if not
Expand All @@ -151,6 +211,81 @@ class QoreURL {
*/
DLLEXPORT int parse(const QoreString* url, bool keep_brackets, ExceptionSink* xsink);

//! parses the URL string passed
/** If a url was already parsed previously, all memory is freed before parsing the new string.
@param url the URL string to parse
@param options a bitfield of %Qore URL options
@return 0 if the URL was parsed successfully, -1 if not
@since %Qore 1.0.10
*/
DLLEXPORT int parse(const char* url, int options);

//! parses the URL string passed
/** If a url was already parsed previously, all memory is freed before parsing the new string.
@param url the URL string to parse
@param options a bitfield of %Qore URL options
@return 0 if the URL was parsed successfully, -1 if not
@since %Qore 1.0.10
*/
DLLEXPORT int parse(const QoreString& url, int options = 0);

//! parses the URL string passed
/** If a url was already parsed previously, all memory is freed before parsing the new string.
@param url the URL string to parse
@param options a bitfield of %Qore URL options
@return 0 if the URL was parsed successfully, -1 if not
@since %Qore 1.0.10
*/
DLLEXPORT int parse(const std::string& url, int options = 0);

//! parses the URL string passed
/** If a url was already parsed previously, all memory is freed before parsing the new string.
@param xsink for Qore-language exceptions
@param url the URL string to parse
@param options a bitfield of %Qore URL options
@return 0 if the URL was parsed successfully, -1 if not
@since %Qore 1.0.10
*/
DLLEXPORT int parse(ExceptionSink* xsink, const char* url, int options = 0);

//! parses the URL string passed
/** If a url was already parsed previously, all memory is freed before parsing the new string.
@param xsink for Qore-language exceptions
@param url the URL string to parse
@param options a bitfield of %Qore URL options
@return 0 if the URL was parsed successfully, -1 if not
@since %Qore 1.0.10
*/
DLLEXPORT int parse(ExceptionSink* xsink, const QoreString& url, int options = 0);

//! parses the URL string passed
/** If a url was already parsed previously, all memory is freed before parsing the new string.
@param xsink for Qore-language exceptions
@param url the URL string to parse
@param options a bitfield of %Qore URL options
@return 0 if the URL was parsed successfully, -1 if not
@since %Qore 1.0.10
*/
DLLEXPORT int parse(ExceptionSink* xsink, const std::string& url, int options = 0);

//! returns true if the URL string parsed is valid
/** @return true if the URL string parsed is valid
*/
Expand Down Expand Up @@ -225,6 +360,20 @@ class QoreURL {
@return a pointer to the hostname (0 if none present), caller owns the memory returned
*/
DLLEXPORT char* take_host();

private:
//! private implementation of the class
struct qore_url_private* priv;

DLLLOCAL void zero();
DLLLOCAL void reset();
DLLLOCAL void parseIntern(const char* url, ExceptionSink* xsink);

//! this function is not implemented; it is here as a private function in order to prohibit it from being used
DLLLOCAL QoreURL(const QoreURL&);

//! this function is not implemented; it is here as a private function in order to prohibit it from being used
DLLLOCAL QoreURL& operator=(const QoreURL&);
};

#endif

0 comments on commit f3d5fb4

Please sign in to comment.