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