libs/http/include/boost/http/json/json_sink.hpp

90.0% Lines (36/40) 75.0% Functions (9/12) 77.8% Branches (14/18)
libs/http/include/boost/http/json/json_sink.hpp
Line Branch Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 //
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)
6 //
7 // Official repository: https://github.com/cppalliance/http
8 //
9
10 #ifndef BOOST_HTTP_JSON_JSON_SINK_HPP
11 #define BOOST_HTTP_JSON_JSON_SINK_HPP
12
13 #include <boost/http/config.hpp>
14
15 #include <boost/capy/buffers.hpp>
16 #include <boost/capy/concept/const_buffer_sequence.hpp>
17 #include <boost/capy/ex/immediate.hpp>
18 #include <boost/capy/io_result.hpp>
19 #include <boost/json/stream_parser.hpp>
20 #include <boost/json/value.hpp>
21
22 namespace boost {
23 namespace http {
24
25 /** A sink for streaming JSON data to a parser.
26
27 This class wraps a `boost::json::stream_parser` and satisfies the
28 @ref capy::WriteSink concept, enabling incremental JSON parsing
29 from any data source that produces buffer sequences.
30
31 Since JSON parsing is synchronous, all operations return
32 @ref capy::immediate awaitables with zero suspension overhead.
33
34 @par Example
35 @code
36 json_sink sink;
37
38 // Write JSON data incrementally
39 auto [ec1, n1] = co_await sink.write(capy::make_buffer("{\"key\":"));
40 auto [ec2, n2] = co_await sink.write(capy::make_buffer("42}"), true);
41
42 // Or use write_eof() separately
43 auto [ec3] = co_await sink.write_eof();
44
45 // Retrieve the parsed value
46 json::value v = sink.release();
47 @endcode
48
49 @par Thread Safety
50 Distinct objects: Safe.
51 Shared objects: Unsafe.
52
53 @see capy::WriteSink, json::stream_parser
54 */
55 class json_sink
56 {
57 json::stream_parser parser_;
58
59 template<capy::ConstBufferSequence CB>
60 capy::immediate<capy::io_result<std::size_t>>
61 12 write_impl(CB const& buffers, bool eof)
62 {
63 12 system::error_code ec;
64 12 std::size_t total = 0;
65 12 auto const end = capy::end(buffers);
66
2/4
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
23 for(auto it = capy::begin(buffers); it != end; ++it)
67 {
68 12 capy::const_buffer buf(*it);
69
1/1
✓ Branch 2 taken 12 times.
24 auto n = parser_.write(
70 12 static_cast<char const*>(buf.data()),
71 buf.size(),
72 ec);
73 12 total += n;
74
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 11 times.
12 if(ec.failed())
75
1/1
✓ Branch 1 taken 1 time.
1 return {ec, total};
76 }
77
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 5 times.
11 if(eof)
78 {
79
1/1
✓ Branch 1 taken 6 times.
6 parser_.finish(ec);
80
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if(ec.failed())
81 return {ec, total};
82 }
83 11 return capy::ready(total);
84 }
85
86 public:
87 /** Default constructor.
88
89 Constructs a sink with a default-initialized stream parser.
90 */
91 8 json_sink() = default;
92
93 /** Constructor with parse options.
94
95 @param opt Options controlling JSON parsing behavior.
96 */
97 explicit
98 1 json_sink(json::parse_options const& opt)
99 1 : parser_(json::storage_ptr(), opt)
100 {
101 1 }
102
103 /** Constructor with storage and parse options.
104
105 @param sp The storage to use for parsed values.
106 @param opt Options controlling JSON parsing behavior.
107 */
108 json_sink(
109 json::storage_ptr sp,
110 json::parse_options const& opt = {})
111 : parser_(std::move(sp), opt)
112 {
113 }
114
115 /** Write some data to the JSON parser.
116
117 Writes bytes from the buffer sequence to the stream parser.
118
119 @param buffers Buffer sequence containing JSON data.
120
121 @return An awaitable yielding `(error_code,std::size_t)`.
122 On success, returns the total bytes written.
123 */
124 template<capy::ConstBufferSequence CB>
125 capy::immediate<capy::io_result<std::size_t>>
126 write_some(CB const& buffers)
127 {
128 return write_impl(buffers, false);
129 }
130
131 /** Write data to the JSON parser.
132
133 Writes all bytes from the buffer sequence to the stream parser.
134
135 @param buffers Buffer sequence containing JSON data.
136
137 @return An awaitable yielding `(error_code,std::size_t)`.
138 On success, returns the total bytes written.
139 */
140 template<capy::ConstBufferSequence CB>
141 capy::immediate<capy::io_result<std::size_t>>
142 5 write(CB const& buffers)
143 {
144 5 return write_impl(buffers, false);
145 }
146
147 /** Write data with optional end-of-stream.
148
149 Writes all bytes from the buffer sequence to the stream parser.
150 If @p eof is true, also finishes parsing.
151
152 @param buffers Buffer sequence containing JSON data.
153 @param eof If true, signals end of JSON data after writing.
154
155 @return An awaitable yielding `(error_code,std::size_t)`.
156 On success, returns the total bytes written.
157 */
158 template<capy::ConstBufferSequence CB>
159 capy::immediate<capy::io_result<std::size_t>>
160 7 write(CB const& buffers, bool eof)
161 {
162 7 return write_impl(buffers, eof);
163 }
164
165 /** Write final data and signal end of JSON data.
166
167 Writes all bytes from the buffer sequence to the stream
168 parser, then finishes parsing.
169
170 @param buffers Buffer sequence containing JSON data.
171
172 @return An awaitable yielding `(error_code,std::size_t)`.
173 On success, returns the total bytes written.
174 */
175 template<capy::ConstBufferSequence CB>
176 capy::immediate<capy::io_result<std::size_t>>
177 write_eof(CB const& buffers)
178 {
179 return write_impl(buffers, true);
180 }
181
182 /** Signal end of JSON data.
183
184 Finishes parsing and validates the JSON is complete.
185
186 @return An awaitable yielding `(error_code)`.
187 */
188 capy::immediate<capy::io_result<>>
189 2 write_eof()
190 {
191 2 system::error_code ec;
192
1/1
✓ Branch 1 taken 2 times.
2 parser_.finish(ec);
193
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
2 if(ec.failed())
194
1/1
✓ Branch 1 taken 1 time.
1 return {ec};
195 1 return {};
196 }
197
198 /** Check if parsing is complete.
199
200 @return `true` if a complete JSON value has been parsed.
201 */
202 bool
203 11 done() const noexcept
204 {
205 11 return parser_.done();
206 }
207
208 /** Release the parsed JSON value.
209
210 Returns the parsed value and resets the parser for reuse.
211
212 @par Preconditions
213 `this->done() == true`
214
215 @return The parsed JSON value.
216 */
217 json::value
218 7 release()
219 {
220 7 return parser_.release();
221 }
222
223 /** Reset the parser for a new JSON value.
224
225 Clears all state and prepares to parse a new value.
226 */
227 void
228 1 reset()
229 {
230 1 parser_.reset();
231 1 }
232 };
233
234 } // namespace http
235 } // namespace boost
236
237 #endif
238