Skip to content
Merged

more #245

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .cursor/rules/doc.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
description: Documentation style conventions for code snippets, javadocs, and API documentation examples
globs:
alwaysApply: false
---
Comment on lines +1 to +5
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Define globs or enable alwaysApply so the rule actually runs.

With alwaysApply: false and an empty globs, this rule likely won’t apply anywhere. Please add explicit doc globs or switch to alwaysApply: true if it should be global.

🤖 Prompt for AI Agents
In @.cursor/rules/doc.mdc around lines 1 - 5, The rule currently has an empty
globs array and alwaysApply: false so it never runs; update the rule by either
populating the globs key with the appropriate file patterns (e.g.,
doc/Javadoc/API examples patterns) or set alwaysApply: true if you intend the
rule to be global, ensuring the 'globs' and 'alwaysApply' fields in the rule
header are adjusted accordingly so the rule will actually execute (update the
'globs' array or toggle 'alwaysApply' in the doc.mdc rule definition).


- `route_params` are named `rp`
60 changes: 51 additions & 9 deletions include/boost/http/server/cors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,73 @@
namespace boost {
namespace http {

/** Options for CORS middleware configuration.
*/
struct cors_options
{
/// Allowed origin, or "*" for any. Empty defaults to "*".
std::string origin;

/// Allowed HTTP methods. Empty defaults to common methods.
std::string methods;

/// Allowed request headers.
std::string allowedHeaders;

/// Response headers exposed to client.
std::string exposedHeaders;

/// Max age for preflight cache.
std::chrono::seconds max_age{ 0 };

/// Status code for preflight response.
status result = status::no_content;

/// If true, pass preflight to next handler.
bool preFlightContinue = false;

/// If true, allow credentials.
bool credentials = false;
};

class cors
/** CORS middleware for handling cross-origin requests.
This middleware handles Cross-Origin Resource Sharing
(CORS) by setting appropriate response headers and
handling preflight OPTIONS requests.
@par Example
@code
cors_options opts;
opts.origin = "*";
opts.methods = "GET,POST,PUT,DELETE";
opts.credentials = true;
router.use( cors( opts ) );
@endcode
@see cors_options
*/
class BOOST_HTTP_DECL cors
{
cors_options options_;

public:
BOOST_HTTP_DECL
explicit cors(
cors_options options = {}) noexcept;
/** Construct a CORS middleware.
BOOST_HTTP_DECL
route_result
operator()(route_params& p) const;
@param options Configuration options.
*/
explicit cors(cors_options options = {}) noexcept;

private:
cors_options options_;
/** Handle a request.
Sets CORS headers and handles preflight requests.
@param rp The route parameters.
@return A task that completes with the routing result.
*/
route_task operator()(route_params& rp) const;
};
Comment on lines +68 to 88
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

std::string move constructor noexcept C++11 C++14 C++17 standard guarantee

💡 Result:

Short answer: The standard never guaranteed that every std::string move constructor is noexcept across C++11/C++14; it was implementation-/form-dependent (allocator forms could throw). The language rules for implicitly‑declared/defaulted move constructors’ exception specification are in the core language (and evolved), but library wording and LWG discussion show basic_string’s move/noexcept was tricky and not an absolute, unconditional guarantee. See cppreference for the language rules and the LWG issues (2069, 2319) about basic_string’s noexcept wording. [1][2][3]

Sources

  • cppreference — move constructors and how their exception specification is determined. [1]
  • C++ LWG issue 2069 (inconsistent exception spec for basic_string move constructor). [2]
  • C++ LWG issue 2319 (discussion about basic_string move constructor noexcept). [3]

🏁 Script executed:

# Search for cors_options definition
rg -n "class cors_options|struct cors_options" include/

Repository: cppalliance/http

Length of output: 117


🏁 Script executed:

# Check for C++ standard configuration
find . -name "CMakeLists.txt" -o -name "cmake.txt" -o -name ".clang-format" -o -name "*.h" -o -name "*.hpp" | head -20 | xargs grep -l "std=c++" 2>/dev/null || echo "Searching for standard flags..."

Repository: cppalliance/http

Length of output: 91


🏁 Script executed:

# Look at the top of the header file for any C++ standard comments or includes
head -50 include/boost/http/server/cors.hpp

Repository: cppalliance/http

Length of output: 1369


Remove noexcept from the constructor or use const reference parameter.

The constructor is marked noexcept but takes cors_options by value, which moves its std::string members (origin, methods, allowedHeaders, exposedHeaders). The std::string move constructor is not unconditionally noexcept—its exception specification depends on the allocator, and this is not guaranteed by the standard in any C++ version. Either remove the noexcept specification, accept const cors_options& instead, or document that only noexcept allocators are supported.

🤖 Prompt for AI Agents
In `@include/boost/http/server/cors.hpp` around lines 68 - 88, The cors
constructor is marked noexcept while taking cors_options by value, which can
trigger non-noexcept std::string moves; change the signature of cors::cors to
either remove the noexcept specifier or accept a const cors_options& parameter
to avoid potentially throwing moves (i.e., adjust the constructor declaration in
class cors to use cors_options const& or drop noexcept), and update any
corresponding implementation to match.


} // http
Expand Down
48 changes: 48 additions & 0 deletions include/boost/http/server/encode_url.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/http
//

#ifndef BOOST_HTTP_SERVER_ENCODE_URL_HPP
#define BOOST_HTTP_SERVER_ENCODE_URL_HPP

#include <boost/http/detail/config.hpp>
#include <boost/core/detail/string_view.hpp>
#include <string>

namespace boost {
namespace http {

/** Percent-encode a URL for safe use in HTTP responses.
Encodes characters that are not safe in URLs using
percent-encoding (e.g. space becomes %20). This is
useful for encoding URLs that will be included in
Location headers or HTML links.
The following characters are NOT encoded:
- Unreserved: A-Z a-z 0-9 - _ . ~
- Reserved (allowed in URLs): ! # $ & ' ( ) * + , / : ; = ? @
@par Example
@code
std::string url = encode_url( "/path/to/file with spaces.txt" );
// url == "/path/to/file%20with%20spaces.txt"
@endcode
@param url The URL or URL component to encode.
@return A new string with unsafe characters percent-encoded.
*/
BOOST_HTTP_DECL
std::string
encode_url(core::string_view url);

} // http
} // boost

#endif
51 changes: 51 additions & 0 deletions include/boost/http/server/escape_html.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/http
//

#ifndef BOOST_HTTP_SERVER_ESCAPE_HTML_HPP
#define BOOST_HTTP_SERVER_ESCAPE_HTML_HPP

#include <boost/http/detail/config.hpp>
#include <boost/core/detail/string_view.hpp>
#include <string>

namespace boost {
namespace http {

/** Escape a string for safe inclusion in HTML.

Replaces characters that have special meaning in HTML
with their corresponding character entity references:

@li `&` becomes `&amp;`
@li `<` becomes `&lt;`
@li `>` becomes `&gt;`
@li `"` becomes `&quot;`
@li `'` becomes `&#39;`

This function is used to prevent XSS (Cross-Site Scripting)
attacks when embedding user input in HTML responses.

@par Example
@code
std::string safe = escape_html( "<script>alert('xss')</script>" );
// safe == "&lt;script&gt;alert(&#39;xss&#39;)&lt;/script&gt;"
@endcode

@param s The string to escape.

@return A new string with HTML special characters escaped.
*/
BOOST_HTTP_DECL
std::string
escape_html(core::string_view s);

} // http
} // boost

#endif
84 changes: 84 additions & 0 deletions include/boost/http/server/etag.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/http
//

#ifndef BOOST_HTTP_SERVER_ETAG_HPP
#define BOOST_HTTP_SERVER_ETAG_HPP

#include <boost/http/detail/config.hpp>
#include <boost/core/detail/string_view.hpp>
#include <cstdint>
#include <string>

namespace boost {
namespace http {

/** Options for ETag generation.
*/
struct etag_options
{
/// Generate a weak ETag (prefixed with W/).
bool weak = false;
};

/** Generate an ETag from content.
Creates an ETag by computing a hash of the provided
content. The resulting ETag can be used in HTTP
responses to enable caching.
@par Example
@code
std::string content = "Hello, World!";
std::string tag = etag( content );
// tag == "\"d-3/1gIbsr1bCvZ2KQgJ7DpTGR3YH\""
@endcode
Comment on lines +35 to +40
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Documentation example may not match actual output format.

The example shows "\"d-3/1gIbsr1bCvZ2KQgJ7DpTGR3YH\"" but the implementation in etag.cpp produces a format like "<size_hex>-<16_hex_chars>" using FNV-1a hash converted to hex. The slash character and base64-like encoding shown here don't match the hex-based implementation.

Consider updating the example to reflect the actual output format, or mark it as illustrative only.

🤖 Prompt for AI Agents
In `@include/boost/http/server/etag.hpp` around lines 35 - 40, The documentation
example for etag() is inconsistent with the implementation in etag.cpp: replace
the base64/slash-style example with one that matches the actual output format
produced by etag (size in hex, a hyphen, then the 16-hex-character FNV-1a hash),
e.g. something like "\"<size_hex>-<16_hex_chars>\"" or, alternatively, add a
note that the example is illustrative only; update the docblock around the etag
function to reflect this change.

@param body The content to hash.
@param opts Options controlling ETag generation.
@return An ETag string suitable for use in the ETag header.
*/
BOOST_HTTP_DECL
std::string
etag(core::string_view body, etag_options opts = {});

/** Generate an ETag from file metadata.
Creates an ETag based on a file's size and modification
time. This is more efficient than hashing file content
and is suitable for static file serving.
@par Example
@code
std::uint64_t size = 1234;
std::uint64_t mtime = 1704067200; // Unix timestamp
std::string tag = etag( size, mtime );
// tag == "\"4d2-65956a00\""
@endcode
@param size The file size in bytes.
@param mtime The file modification time (typically Unix timestamp).
@param opts Options controlling ETag generation.
@return An ETag string suitable for use in the ETag header.
*/
BOOST_HTTP_DECL
std::string
etag(
std::uint64_t size,
std::uint64_t mtime,
etag_options opts = {});

} // http
} // boost

#endif
65 changes: 65 additions & 0 deletions include/boost/http/server/fresh.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/http
//

#ifndef BOOST_HTTP_SERVER_FRESH_HPP
#define BOOST_HTTP_SERVER_FRESH_HPP

#include <boost/http/detail/config.hpp>
#include <boost/http/request.hpp>
#include <boost/http/response.hpp>

namespace boost {
namespace http {

/** Check if a response is fresh for conditional GET.

Compares the request's conditional headers (`If-None-Match`
and `If-Modified-Since`) against the response's caching
headers (`ETag` and `Last-Modified`) to determine if the
cached response is still valid.

If this returns `true`, the server should respond with
304 Not Modified instead of sending the full response body.

@par Example
@code
// Prepare response headers
rp.res.set( field::etag, "\"abc123\"" );
rp.res.set( field::last_modified, "Wed, 21 Oct 2024 07:28:00 GMT" );

// Check freshness
if( is_fresh( rp.req, rp.res ) )
{
rp.status( status::not_modified );
co_return co_await rp.send( "" );
}

// Send full response
co_return co_await rp.send( content );
@endcode

@param req The HTTP request.

@param res The HTTP response (with ETag/Last-Modified set).

@return `true` if the response is fresh (304 should be sent),
`false` if the full response should be sent.

@see http::field::if_none_match, http::field::if_modified_since
*/
BOOST_HTTP_DECL
bool
is_fresh(
request const& req,
response const& res) noexcept;

} // http
} // boost

#endif
Loading
Loading