54.24% Lines (32/59)
90.91% Functions (10/11)
| TLA | Baseline | Branch | ||||||
|---|---|---|---|---|---|---|---|---|
| Line | Hits | Code | Line | Hits | Code | |||
| 1 | // | 1 | // | |||||
| 2 | // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) | 2 | // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) | |||||
| 3 | // | 3 | // | |||||
| 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |||||
| 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |||||
| 6 | // | 6 | // | |||||
| 7 | // Official repository: https://github.com/cppalliance/http | 7 | // Official repository: https://github.com/cppalliance/http | |||||
| 8 | // | 8 | // | |||||
| 9 | 9 | |||||||
| 10 | #include <boost/http/server/cors.hpp> | 10 | #include <boost/http/server/cors.hpp> | |||||
| 11 | #include <utility> | 11 | #include <utility> | |||||
| 12 | 12 | |||||||
| 13 | namespace boost { | 13 | namespace boost { | |||||
| 14 | namespace http { | 14 | namespace http { | |||||
| 15 | 15 | |||||||
| HITCBC | 16 | 1 | cors:: | 16 | 1 | cors:: | ||
| 17 | cors( | 17 | cors( | |||||
| HITCBC | 18 | 1 | cors_options options) noexcept | 18 | 1 | cors_options options) noexcept | ||
| HITCBC | 19 | 1 | : options_(std::move(options)) | 19 | 1 | : options_(std::move(options)) | ||
| 20 | { | 20 | { | |||||
| 21 | // VFALCO TODO Validate the strings in options against RFC | 21 | // VFALCO TODO Validate the strings in options against RFC | |||||
| HITCBC | 22 | 1 | } | 22 | 1 | } | ||
| 23 | 23 | |||||||
| 24 | namespace { | 24 | namespace { | |||||
| 25 | 25 | |||||||
| 26 | struct Vary | 26 | struct Vary | |||||
| 27 | { | 27 | { | |||||
| HITCBC | 28 | 1 | Vary(route_params& rp) | 28 | 1 | Vary(route_params& rp) | ||
| HITCBC | 29 | 1 | : rp_(rp) | 29 | 1 | : rp_(rp) | ||
| 30 | { | 30 | { | |||||
| HITCBC | 31 | 1 | } | 31 | 1 | } | ||
| 32 | 32 | |||||||
| HITCBC | 33 | 2 | void set(field f, core::string_view s) | 33 | 2 | void set(field f, core::string_view s) | ||
| 34 | { | 34 | { | |||||
| HITCBC | 35 | 2 | rp_.res.set(f, s); | 35 | 2 | rp_.res.set(f, s); | ||
| HITCBC | 36 | 2 | } | 36 | 2 | } | ||
| 37 | 37 | |||||||
| MISUBC | 38 | ✗ | void append(field f, core::string_view v) | 38 | ✗ | void append(field f, core::string_view v) | ||
| 39 | { | 39 | { | |||||
| MISUBC | 40 | ✗ | auto it = rp_.res.find(f); | 40 | ✗ | auto it = rp_.res.find(f); | ||
| MISUBC | 41 | ✗ | if(it != rp_.res.end()) | 41 | ✗ | if(it != rp_.res.end()) | ||
| 42 | { | 42 | { | |||||
| MISUBC | 43 | ✗ | std::string s = it->value; | 43 | ✗ | std::string s = it->value; | ||
| MISUBC | 44 | ✗ | s += ", "; | 44 | ✗ | s += ", "; | ||
| MISUBC | 45 | ✗ | s += v; | 45 | ✗ | s += v; | ||
| MISUBC | 46 | ✗ | rp_.res.set(it, s); | 46 | ✗ | rp_.res.set(it, s); | ||
| MISUBC | 47 | ✗ | } | 47 | ✗ | } | ||
| 48 | else | 48 | else | |||||
| 49 | { | 49 | { | |||||
| MISUBC | 50 | ✗ | rp_.res.set(f, v); | 50 | ✗ | rp_.res.set(f, v); | ||
| 51 | } | 51 | } | |||||
| MISUBC | 52 | ✗ | } | 52 | ✗ | } | ||
| 53 | 53 | |||||||
| 54 | private: | 54 | private: | |||||
| 55 | route_params& rp_; | 55 | route_params& rp_; | |||||
| 56 | }; | 56 | }; | |||||
| 57 | 57 | |||||||
| 58 | } // (anon) | 58 | } // (anon) | |||||
| 59 | 59 | |||||||
| 60 | // Access-Control-Allow-Origin | 60 | // Access-Control-Allow-Origin | |||||
| HITCBC | 61 | 1 | static void setOrigin( | 61 | 1 | static void setOrigin( | ||
| 62 | Vary& v, | 62 | Vary& v, | |||||
| 63 | route_params const&, | 63 | route_params const&, | |||||
| 64 | cors_options const& options) | 64 | cors_options const& options) | |||||
| 65 | { | 65 | { | |||||
| HITCBC | 66 | 1 | if( options.origin.empty() || | 66 | 1 | if( options.origin.empty() || | ||
| MISUBC | 67 | ✗ | options.origin == "*") | 67 | ✗ | options.origin == "*") | ||
| 68 | { | 68 | { | |||||
| HITCBC | 69 | 1 | v.set(field::access_control_allow_origin, "*"); | 69 | 1 | v.set(field::access_control_allow_origin, "*"); | ||
| HITCBC | 70 | 1 | return; | 70 | 1 | return; | ||
| 71 | } | 71 | } | |||||
| 72 | 72 | |||||||
| MISUBC | 73 | ✗ | v.set( | 73 | ✗ | v.set( | ||
| 74 | field::access_control_allow_origin, | 74 | field::access_control_allow_origin, | |||||
| MISUBC | 75 | ✗ | options.origin); | 75 | ✗ | options.origin); | ||
| MISUBC | 76 | ✗ | v.append(field::vary, to_string(field::origin)); | 76 | ✗ | v.append(field::vary, to_string(field::origin)); | ||
| 77 | } | 77 | } | |||||
| 78 | 78 | |||||||
| 79 | // Access-Control-Allow-Methods | 79 | // Access-Control-Allow-Methods | |||||
| HITCBC | 80 | 1 | static void setMethods( | 80 | 1 | static void setMethods( | ||
| 81 | Vary& v, | 81 | Vary& v, | |||||
| 82 | cors_options const& options) | 82 | cors_options const& options) | |||||
| 83 | { | 83 | { | |||||
| HITCBC | 84 | 1 | if(! options.methods.empty()) | 84 | 1 | if(! options.methods.empty()) | ||
| 85 | { | 85 | { | |||||
| MISUBC | 86 | ✗ | v.set( | 86 | ✗ | v.set( | ||
| 87 | field::access_control_allow_methods, | 87 | field::access_control_allow_methods, | |||||
| MISUBC | 88 | ✗ | options.methods); | 88 | ✗ | options.methods); | ||
| MISUBC | 89 | ✗ | return; | 89 | ✗ | return; | ||
| 90 | } | 90 | } | |||||
| HITCBC | 91 | 1 | v.set( | 91 | 1 | v.set( | ||
| 92 | field::access_control_allow_methods, | 92 | field::access_control_allow_methods, | |||||
| 93 | "GET,HEAD,PUT,PATCH,POST,DELETE"); | 93 | "GET,HEAD,PUT,PATCH,POST,DELETE"); | |||||
| 94 | } | 94 | } | |||||
| 95 | 95 | |||||||
| 96 | // Access-Control-Allow-Credentials | 96 | // Access-Control-Allow-Credentials | |||||
| HITCBC | 97 | 1 | static void setCredentials( | 97 | 1 | static void setCredentials( | ||
| 98 | Vary& v, | 98 | Vary& v, | |||||
| 99 | cors_options const& options) | 99 | cors_options const& options) | |||||
| 100 | { | 100 | { | |||||
| HITCBC | 101 | 1 | if(! options.credentials) | 101 | 1 | if(! options.credentials) | ||
| HITCBC | 102 | 1 | return; | 102 | 1 | return; | ||
| MISUBC | 103 | ✗ | v.set( | 103 | ✗ | v.set( | ||
| 104 | field::access_control_allow_credentials, | 104 | field::access_control_allow_credentials, | |||||
| 105 | "true"); | 105 | "true"); | |||||
| 106 | } | 106 | } | |||||
| 107 | 107 | |||||||
| 108 | // Access-Control-Allowed-Headers | 108 | // Access-Control-Allowed-Headers | |||||
| HITCBC | 109 | 1 | static void setAllowedHeaders( | 109 | 1 | static void setAllowedHeaders( | ||
| 110 | Vary& v, | 110 | Vary& v, | |||||
| 111 | route_params const& rp, | 111 | route_params const& rp, | |||||
| 112 | cors_options const& options) | 112 | cors_options const& options) | |||||
| 113 | { | 113 | { | |||||
| HITCBC | 114 | 1 | if(! options.allowedHeaders.empty()) | 114 | 1 | if(! options.allowedHeaders.empty()) | ||
| 115 | { | 115 | { | |||||
| MISUBC | 116 | ✗ | v.set( | 116 | ✗ | v.set( | ||
| 117 | field::access_control_allow_headers, | 117 | field::access_control_allow_headers, | |||||
| MISUBC | 118 | ✗ | options.allowedHeaders); | 118 | ✗ | options.allowedHeaders); | ||
| MISUBC | 119 | ✗ | return; | 119 | ✗ | return; | ||
| 120 | } | 120 | } | |||||
| HITCBC | 121 | 1 | auto s = rp.req.value_or( | 121 | 1 | auto s = rp.req.value_or( | ||
| 122 | field::access_control_request_headers, ""); | 122 | field::access_control_request_headers, ""); | |||||
| HITCBC | 123 | 1 | if(! s.empty()) | 123 | 1 | if(! s.empty()) | ||
| 124 | { | 124 | { | |||||
| MISUBC | 125 | ✗ | v.set(field::access_control_allow_headers, s); | 125 | ✗ | v.set(field::access_control_allow_headers, s); | ||
| MISUBC | 126 | ✗ | v.append(field::vary, s); | 126 | ✗ | v.append(field::vary, s); | ||
| 127 | } | 127 | } | |||||
| 128 | } | 128 | } | |||||
| 129 | 129 | |||||||
| 130 | // Access-Control-Expose-Headers | 130 | // Access-Control-Expose-Headers | |||||
| HITCBC | 131 | 1 | static void setExposeHeaders( | 131 | 1 | static void setExposeHeaders( | ||
| 132 | Vary& v, | 132 | Vary& v, | |||||
| 133 | cors_options const& options) | 133 | cors_options const& options) | |||||
| 134 | { | 134 | { | |||||
| HITCBC | 135 | 1 | if(options.exposedHeaders.empty()) | 135 | 1 | if(options.exposedHeaders.empty()) | ||
| HITCBC | 136 | 1 | return; | 136 | 1 | return; | ||
| MISUBC | 137 | ✗ | v.set( | 137 | ✗ | v.set( | ||
| 138 | field::access_control_expose_headers, | 138 | field::access_control_expose_headers, | |||||
| MISUBC | 139 | ✗ | options.exposedHeaders); | 139 | ✗ | options.exposedHeaders); | ||
| 140 | } | 140 | } | |||||
| 141 | 141 | |||||||
| 142 | // Access-Control-Max-Age | 142 | // Access-Control-Max-Age | |||||
| HITCBC | 143 | 1 | static void setMaxAge( | 143 | 1 | static void setMaxAge( | ||
| 144 | Vary& v, | 144 | Vary& v, | |||||
| 145 | cors_options const& options) | 145 | cors_options const& options) | |||||
| 146 | { | 146 | { | |||||
| HITCBC | 147 | 1 | if(options.max_age.count() == 0) | 147 | 1 | if(options.max_age.count() == 0) | ||
| HITCBC | 148 | 1 | return; | 148 | 1 | return; | ||
| MISUBC | 149 | ✗ | v.set( | 149 | ✗ | v.set( | ||
| 150 | field::access_control_max_age, | 150 | field::access_control_max_age, | |||||
| MISUBC | 151 | ✗ | std::to_string( | 151 | ✗ | std::to_string( | ||
| 152 | options.max_age.count())); | 152 | options.max_age.count())); | |||||
| 153 | } | 153 | } | |||||
| 154 | 154 | |||||||
| 155 | route_task | 155 | route_task | |||||
| HITCBC | 156 | 1 | cors:: | 156 | 1 | cors:: | ||
| 157 | operator()( | 157 | operator()( | |||||
| 158 | route_params& rp) const | 158 | route_params& rp) const | |||||
| 159 | { | 159 | { | |||||
| 160 | Vary v(rp); | 160 | Vary v(rp); | |||||
| 161 | if(rp.req.method() == method::options) | 161 | if(rp.req.method() == method::options) | |||||
| 162 | { | 162 | { | |||||
| 163 | // preflight | 163 | // preflight | |||||
| 164 | setOrigin(v, rp, options_); | 164 | setOrigin(v, rp, options_); | |||||
| 165 | setMethods(v, options_); | 165 | setMethods(v, options_); | |||||
| 166 | setCredentials(v, options_); | 166 | setCredentials(v, options_); | |||||
| 167 | setAllowedHeaders(v, rp, options_); | 167 | setAllowedHeaders(v, rp, options_); | |||||
| 168 | setMaxAge(v, options_); | 168 | setMaxAge(v, options_); | |||||
| 169 | setExposeHeaders(v, options_); | 169 | setExposeHeaders(v, options_); | |||||
| 170 | 170 | |||||||
| 171 | if(options_.preFlightContinue) | 171 | if(options_.preFlightContinue) | |||||
| 172 | co_return route_next; | 172 | co_return route_next; | |||||
| 173 | 173 | |||||||
| 174 | // Safari and others need this for 204 or may hang | 174 | // Safari and others need this for 204 or may hang | |||||
| 175 | rp.res.set_status(options_.result); | 175 | rp.res.set_status(options_.result); | |||||
| 176 | auto [ec] = co_await rp.send(""); | 176 | auto [ec] = co_await rp.send(""); | |||||
| 177 | if(ec) | 177 | if(ec) | |||||
| 178 | co_return route_error(ec); | 178 | co_return route_error(ec); | |||||
| 179 | co_return route_done; | 179 | co_return route_done; | |||||
| 180 | } | 180 | } | |||||
| 181 | 181 | |||||||
| 182 | // actual response | 182 | // actual response | |||||
| 183 | setOrigin(v, rp, options_); | 183 | setOrigin(v, rp, options_); | |||||
| 184 | setCredentials(v, options_); | 184 | setCredentials(v, options_); | |||||
| 185 | setExposeHeaders(v, options_); | 185 | setExposeHeaders(v, options_); | |||||
| 186 | co_return route_next; | 186 | co_return route_next; | |||||
| HITCBC | 187 | 2 | } | 187 | 2 | } | ||
| 188 | 188 | |||||||
| 189 | } // http | 189 | } // http | |||||
| 190 | } // boost | 190 | } // boost | |||||