From 8987c926ca2aee5d954b8f9b8fcfce0a4761a054 Mon Sep 17 00:00:00 2001 From: janbar Date: Sat, 25 Nov 2023 14:25:26 +0100 Subject: [PATCH] prepare redirection request --- noson/src/private/uriparser.cpp | 29 ++++++++++++---- noson/src/private/uriparser.h | 4 ++- noson/src/private/wsrequest.cpp | 60 +++++++++++++++++++++++++++++++++ noson/src/private/wsrequest.h | 5 +++ 4 files changed, 91 insertions(+), 7 deletions(-) diff --git a/noson/src/private/uriparser.cpp b/noson/src/private/uriparser.cpp index 30494de..3da98fb 100644 --- a/noson/src/private/uriparser.cpp +++ b/noson/src/private/uriparser.cpp @@ -48,15 +48,11 @@ void URIParser::URIScan(char *uri, URI_t *parts) char *after_scheme = uri; memset(parts, '\0', sizeof(URI_t)); - /* look for fragment identifier */ - if ((p = strchr(uri, '#')) != NULL) - { - *p = '\0'; - parts->fragment = ++p; - } + /* space is not allowed, therefore break on space */ if ((p = strchr(uri, ' ')) != NULL) *p = '\0'; + /* first look for scheme */ for (p = after_scheme; *p; p++) { if (*p == '/' || *p == '#' || *p == '?') @@ -82,6 +78,7 @@ void URIParser::URIScan(char *uri, URI_t *parts) } } + /* parse the rest after scheme */ p = after_scheme; if (*p == '/') { @@ -146,4 +143,24 @@ void URIParser::URIScan(char *uri, URI_t *parts) /* NULL for "" */ parts->relative = (*after_scheme) ? after_scheme : NULL; } + + /* parse fragment from path */ + if (parts->relative) + after_scheme = parts->relative; + else if (parts->absolute) + after_scheme = parts->absolute; + + /* look for fragment identifier */ + if ((p = strchr(after_scheme, '#')) != NULL) + { + *p = '\0'; + parts->fragment = ++p; + after_scheme = p; + } + /* parse params to follow */ + if ((p = strchr(after_scheme, '?')) != NULL) + { + *p = '\0'; + parts->params = ++p; + } } diff --git a/noson/src/private/uriparser.h b/noson/src/private/uriparser.h index da11bf6..9615d2d 100644 --- a/noson/src/private/uriparser.h +++ b/noson/src/private/uriparser.h @@ -27,7 +27,7 @@ namespace NSROOT { - + class URIParser { public: @@ -42,6 +42,7 @@ namespace NSROOT bool IsRelative() const { return m_parts.relative ? true : false; } const char *Path() const { return IsRelative() ? m_parts.relative : m_parts.absolute; } const char *Fragment() const { return m_parts.fragment; } + const char *Params() const { return m_parts.params; } private: // prevent copy @@ -58,6 +59,7 @@ namespace NSROOT char *absolute; char *relative; char *fragment; + char *params; } URI_t; URI_t m_parts; diff --git a/noson/src/private/wsrequest.cpp b/noson/src/private/wsrequest.cpp index 4185462..41fdc3f 100644 --- a/noson/src/private/wsrequest.cpp +++ b/noson/src/private/wsrequest.cpp @@ -100,6 +100,12 @@ WSRequest::WSRequest(const URIParser& uri, HRM_t method) if (uri.Path()) m_service_url.append(uri.Path()); + if (uri.Fragment()) + m_service_url.append("#").append(uri.Fragment()); + + if (uri.Params()) + m_contentData.append(uri.Params()); + // by default allow content encoding if possible RequestAcceptEncoding(true); } @@ -108,6 +114,60 @@ WSRequest::~WSRequest() { } +WSRequest::WSRequest(const WSRequest& o, const URIParser& redirection) +: m_server(o.m_server) +, m_port(o.m_port) +, m_secure_uri(o.m_secure_uri) +, m_service_method(o.m_service_method) +, m_charset(o.m_charset) +, m_accept(o.m_accept) +, m_contentType(o.m_contentType) +, m_contentData(o.m_contentData) +, m_headers(o.m_headers) +, m_userAgent(o.m_userAgent) +{ + /* The "Location" header field is used in some responses to refer to a + * specific resource in relation to the response. The type of relationship + * is defined by the combination of request method and status code semantics. + */ + if (redirection.Host()) + m_server.assign(redirection.Host()); + + if (redirection.Scheme()) + { + if (strncmp(redirection.Scheme(), "https", 5) == 0) + { + m_secure_uri = true; + m_port = redirection.Port() ? redirection.Port() : 443; + } + else + { + m_secure_uri = false; + m_port = redirection.Port() ? redirection.Port() : 80; + } + } + + URIParser o_uri(m_service_url); + m_service_url = "/"; + if (redirection.Path()) + m_service_url.append(redirection.Path()); + + /* If the Location value provided in a 3xx (Redirection) response does not have + * a fragment component, a user agent MUST process the redirection as if the + * value inherits the fragment component of the URI reference used to generate + * the target URI (i.e., the redirection inherits the original reference's + * fragment, if any). + */ + if (redirection.Fragment()) + m_service_url.append("#").append(redirection.Fragment()); + else if (o_uri.Fragment()) + m_service_url.append("#").append(o_uri.Fragment()); + + /* params have been copied from original request (content data), therefore + * those specified in the new location are ignored + */ +} + void WSRequest::RequestService(const std::string& url, HRM_t method) { m_service_url = url; diff --git a/noson/src/private/wsrequest.h b/noson/src/private/wsrequest.h index f30b405..f3c9d55 100644 --- a/noson/src/private/wsrequest.h +++ b/noson/src/private/wsrequest.h @@ -56,6 +56,9 @@ namespace NSROOT WSRequest(const URIParser& uri, HRM_t method = HRM_GET); ~WSRequest(); + // Clone for redirection: see RFC-9110 section 10.2.2 Location + WSRequest(const WSRequest& o, const URIParser& redirection); + void RequestService(const std::string& url, HRM_t method = HRM_GET); void RequestAccept(CT_t contentType); void RequestAcceptEncoding(bool yesno); @@ -71,6 +74,8 @@ namespace NSROOT const std::string& GetServer() const { return m_server; } unsigned GetPort() const { return m_port; } bool IsSecureURI() const { return m_secure_uri; } + HRM_t GetMethod() const { return m_service_method; } + const std::string& GetURL() const { return m_service_url; } private: std::string m_server;