From 217f42b7c60b0cc18712fa8c8f426f2f69463012 Mon Sep 17 00:00:00 2001 From: Tomasz Sowa Date: Tue, 30 Aug 2022 01:52:02 +0200 Subject: [PATCH] add support for preflight requ (cors) --- winixd/core/header.h | 4 ++ winixd/core/request.cpp | 13 ++++ winixd/core/request.h | 1 + winixd/functions/functionbase.cpp | 98 ++++++++++++++++++++++++++++++- winixd/functions/functionbase.h | 10 ++++ 5 files changed, 125 insertions(+), 1 deletion(-) diff --git a/winixd/core/header.h b/winixd/core/header.h index 90c69cd..023f59c 100644 --- a/winixd/core/header.h +++ b/winixd/core/header.h @@ -54,6 +54,10 @@ public: static constexpr const wchar_t * accept_language = L"Accept-Language"; static constexpr const wchar_t * authorization = L"Authorization"; static constexpr const wchar_t * allow = L"Allow"; + static constexpr const wchar_t * access_control_allow_methods = L"Access-Control-Allow-Methods"; + static constexpr const wchar_t * access_control_allow_origin = L"Access-Control-Allow-Origin"; + static constexpr const wchar_t * access_control_allow_headers = L"Access-Control-Allow-Headers"; + static constexpr const wchar_t * access_control_max_age = L"Access-Control-Max-Age"; /* * headers' names lower case diff --git a/winixd/core/request.cpp b/winixd/core/request.cpp index a8790c6..e2e74cc 100644 --- a/winixd/core/request.cpp +++ b/winixd/core/request.cpp @@ -1597,6 +1597,19 @@ return false; } +bool Request::AddHeader(const wchar_t * name, long value) +{ + if( !out_headers.has_key(name) ) + { + out_headers.add(name, value); + return true; + } + +return false; +} + + + bool Request::AddHeader(const std::wstring & name, const std::wstring & value) { if( !out_headers.has_key(name) ) diff --git a/winixd/core/request.h b/winixd/core/request.h index 665f221..6eda69a 100644 --- a/winixd/core/request.h +++ b/winixd/core/request.h @@ -513,6 +513,7 @@ public: // RENAMEME to add_header_if_not_exists bool AddHeader(const wchar_t * name, const wchar_t * value); + bool AddHeader(const wchar_t * name, long value); bool AddHeader(const std::wstring & name, const std::wstring & value); bool AddHeader(const wchar_t * name, const pt::WTextStream & value); bool AddHeader(const std::wstring & name, const pt::WTextStream & value); diff --git a/winixd/functions/functionbase.cpp b/winixd/functions/functionbase.cpp index fd64ed3..d154aab 100644 --- a/winixd/functions/functionbase.cpp +++ b/winixd/functions/functionbase.cpp @@ -142,6 +142,61 @@ bool FunctionBase::HasAccess() } +bool FunctionBase::IsCorsMethodAvailable(Request::Method method) +{ + return method == Request::get || method == Request::head || method == Request::post || method == Request::put || + method == Request::delete_ ||method == Request::patch; +} + + +bool FunctionBase::IsCorsOriginAvailable(const std::wstring & origin_url) +{ + // true by default for all urles + return true; +} + + +bool FunctionBase::AreCorsHeadersAvailable(const std::wstring & headers) +{ + // true by default for all headers + // headers are comma separated + return true; +} + + +/* + * method is the value of Access-Control-Request-Method header sent by the client + */ +void FunctionBase::AddAccessControlAllowMethodsHeader(Request::Method method) +{ + cur->request->AddHeader(Header::access_control_allow_methods, L"GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH"); +} + + +/* + * origin_url is the value of Origin header sent by the client + */ +void FunctionBase::AddAccessControlAllowOriginHeader(const std::wstring & origin_url) +{ + cur->request->AddHeader(Header::access_control_allow_origin, origin_url); +} + + +/* + * headers is the value of Access-Control-Request-Headers header sent by the client + */ +void FunctionBase::AddAccessControlAllowHeadersHeader(const std::wstring & headers) +{ + cur->request->AddHeader(Header::access_control_allow_headers, headers); +} + + +void FunctionBase::AddAccessControlMaxAgeHeader() +{ + // default 24 hours + cur->request->AddHeader(Header::access_control_max_age, 86400); +} + void FunctionBase::MakeGet() { @@ -174,12 +229,53 @@ void FunctionBase::MakeConnect() // do nothing by default } + void FunctionBase::MakeOptions() { cur->request->http_status = Header::status_204_no_content; - cur->request->out_headers.add(Header::allow, L"OPTIONS, GET, HEAD, POST, DELETE"); + + pt::Space * cors_method = cur->request->headers_in.get_space_nc(L"Access_Control_Request_Method"); // FastCGI changes '-' to '_' + pt::Space * cors_headers = cur->request->headers_in.get_space_nc(L"Access_Control_Request_Headers"); + pt::Space * cors_origin = cur->request->headers_in.get_space_nc(L"Origin"); + + if( cors_method && cors_origin && cors_method->is_wstr() && cors_origin->is_wstr() ) + { + /* + * this is a preflight request + * https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request + * (we allow Access-Control-Request-Headers not to be present) + */ + Request::Method method = Request::CheckRequestMethod(cors_method->get_wstr()->c_str()); + + if( IsCorsMethodAvailable(method) && IsCorsOriginAvailable(*cors_origin->get_wstr()) ) + { + bool cors_available = true; + + if( cors_headers && cors_headers->is_wstr() ) + { + cors_available = AreCorsHeadersAvailable(*cors_headers->get_wstr()); + } + + if( cors_available ) + { + AddAccessControlAllowMethodsHeader(method); + AddAccessControlAllowOriginHeader(*cors_origin->get_wstr()); + AddAccessControlMaxAgeHeader(); + + if( cors_headers && cors_headers->is_wstr() ) + { + AddAccessControlAllowHeadersHeader(*cors_headers->get_wstr()); + } + } + } + } + else + { + cur->request->out_headers.add(Header::allow, L"GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH"); + } } + void FunctionBase::MakeTrace() { // do nothing by default diff --git a/winixd/functions/functionbase.h b/winixd/functions/functionbase.h index 4bf5dd4..dec23c8 100644 --- a/winixd/functions/functionbase.h +++ b/winixd/functions/functionbase.h @@ -111,6 +111,16 @@ public: virtual bool HasAccess(); + virtual bool IsCorsMethodAvailable(Request::Method method); + virtual bool IsCorsOriginAvailable(const std::wstring & origin_url); + virtual bool AreCorsHeadersAvailable(const std::wstring & headers); + + virtual void AddAccessControlAllowMethodsHeader(Request::Method method); + virtual void AddAccessControlAllowOriginHeader(const std::wstring & origin_url); + virtual void AddAccessControlAllowHeadersHeader(const std::wstring & headers); + virtual void AddAccessControlMaxAgeHeader(); + + virtual void MakeGet(); virtual void MakeHead(); virtual void MakePost();