Skip to content

Conversation

Copilot
Copy link

@Copilot Copilot AI commented Oct 19, 2025

Overview

This PR implements operator<< for HTTP message types (fields, fields_base, request, request_base, response, response_base) with automatic CRLF to LF conversion and removal of trailing CRLF.

Problem

Previously, only fields_base had an operator<< that simply output the raw buffer containing CRLF line endings and a trailing CRLF (which marks the end of HTTP headers). This made the output unsuitable for logging, debugging, and display purposes:

http_proto::request req(http_proto::method::get, "/");
req.set(http_proto::field::host, "example.com");
std::cout << req.buffer();
// Output:
// GET / HTTP/1.1\r\n
// Host: example.com\r\n
// \r\n
// (ends with trailing blank line)

Solution

This PR modifies the existing operator<< for fields_base and adds new operator<< implementations for all derived types. The new implementation:

  1. Converts CRLF to LF: All \r\n sequences are replaced with \n for standard Unix-style line endings
  2. Removes trailing CRLF: The final \r\n that indicates the end of headers is not output
http_proto::request req(http_proto::method::get, "/");
req.set(http_proto::field::host, "example.com");
std::cout << req;
// Output:
// GET / HTTP/1.1
// Host: example.com
// (clean output, no trailing newline)

Implementation Details

The core conversion logic is in fields_base::operator<<:

std::ostream& operator<<(std::ostream& os, const fields_base& f)
{
    auto buf = f.buffer();
    std::size_t i = 0;
    
    while (i < buf.size()) {
        if (i + 1 < buf.size() && buf[i] == '\r' && buf[i+1] == '\n') {
            if (i + 2 == buf.size()) {
                // Trailing CRLF, don't output it
                break;
            }
            os << '\n';  // Replace CRLF with LF
            i += 2;
        } else {
            os << buf[i];
            i++;
        }
    }
    return os;
}

All derived type implementations simply forward to the base implementation to maintain consistency.

Benefits

  • ✅ Clean, readable output for logging and debugging
  • ✅ Standard LF line endings (Unix-style)
  • ✅ No trailing blank line from HTTP terminator CRLF
  • ✅ Works with all stream types (std::cout, std::ofstream, std::stringstream)
  • ✅ Backward compatible - .buffer() still available for raw access

Testing

Added comprehensive test suite in test/unit/ostream.cpp covering:

  • All supported types (fields, request, response, and their base classes)
  • Empty messages
  • Messages with multiple fields
  • Edge cases (empty strings, no CRLF, etc.)

Fixes #<issue_number>

Original prompt

This section details on the original issue you should resolve

<issue_title>operator<< for fields and messages</issue_title>
<issue_description>These need to use a single newline for each line instead of the CRLF which is part of the serialized data. And of course do not print the trailing CRLF which indicates the end of the headers. The exact list of types which need std::ostream support are: request, response, request_base, response_base, fields, fields_base
</issue_description>

Comments on the Issue (you are @copilot in this section)

Fixes #182


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

@Copilot Copilot AI changed the title [WIP] Update operator<< for fields and messages Add operator<< with CRLF to LF conversion for fields and messages Oct 19, 2025
@Copilot Copilot AI requested a review from vinniefalco October 19, 2025 15:19
Copilot finished work on behalf of vinniefalco October 19, 2025 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

operator<< for fields and messages

2 participants