2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
19 * Copyright 2015 Cloudius Systems
22 #include <seastar/http/common.hh>
23 #include <seastar/core/iostream-impl.hh>
29 operation_type
str2type(const sstring
& type
) {
30 if (type
== "DELETE") {
42 if (type
== "OPTIONS") {
45 if (type
== "TRACE") {
48 if (type
== "CONNECT") {
54 sstring
type2str(operation_type type
) {
67 if (type
== OPTIONS
) {
73 if (type
== CONNECT
) {
84 static constexpr size_t default_body_sink_buffer_size
= 32000;
86 // Data sinks below are running "on top" of socket output stream and provide
87 // reliable and handy way of generating request bodies according to selected
88 // encoding type and content-length.
90 // Respectively, both .close() methods should not close the underlying stream,
91 // because the socket in question may continue being in use for keep-alive
92 // connections, and closing it would just break the keep-alive-ness
94 class http_chunked_data_sink_impl
: public data_sink_impl
{
95 output_stream
<char>& _out
;
97 future
<> write_size(size_t s
) {
98 auto req
= format("{:x}\r\n", s
);
99 return _out
.write(req
);
102 http_chunked_data_sink_impl(output_stream
<char>& out
) : _out(out
) {
104 virtual future
<> put(net::packet data
) override
{ abort(); }
105 using data_sink_impl::put
;
106 virtual future
<> put(temporary_buffer
<char> buf
) override
{
107 if (buf
.size() == 0) {
108 // size 0 buffer should be ignored, some server
109 // may consider it an end of message
110 return make_ready_future
<>();
112 auto size
= buf
.size();
113 return write_size(size
).then([this, buf
= std::move(buf
)] () mutable {
114 return _out
.write(buf
.get(), buf
.size());
115 }).then([this] () mutable {
116 return _out
.write("\r\n", 2);
119 virtual future
<> close() override
{
120 return make_ready_future
<>();
124 class http_chunked_data_sink
: public data_sink
{
126 http_chunked_data_sink(output_stream
<char>& out
)
127 : data_sink(std::make_unique
<http_chunked_data_sink_impl
>(
131 output_stream
<char> make_http_chunked_output_stream(output_stream
<char>& out
) {
132 output_stream_options opts
;
133 opts
.trim_to_size
= true;
134 return output_stream
<char>(http_chunked_data_sink(out
), default_body_sink_buffer_size
, opts
);
137 class http_content_length_data_sink_impl
: public data_sink_impl
{
138 output_stream
<char>& _out
;
140 size_t& _bytes_written
;
143 http_content_length_data_sink_impl(output_stream
<char>& out
, size_t& len
)
145 , _limit(std::exchange(len
, 0))
146 , _bytes_written(len
)
149 virtual future
<> put(net::packet data
) override
{ abort(); }
150 using data_sink_impl::put
;
151 virtual future
<> put(temporary_buffer
<char> buf
) override
{
152 if (buf
.size() == 0 || _bytes_written
== _limit
) {
153 return make_ready_future
<>();
156 auto size
= buf
.size();
157 if (_bytes_written
+ size
> _limit
) {
158 return make_exception_future
<>(std::runtime_error(format("body conent length overflow: want {} limit {}", _bytes_written
+ buf
.size(), _limit
)));
161 return _out
.write(buf
.get(), size
).then([this, size
] {
162 _bytes_written
+= size
;
165 virtual future
<> close() override
{
166 return make_ready_future
<>();
170 class http_content_length_data_sink
: public data_sink
{
172 http_content_length_data_sink(output_stream
<char>& out
, size_t& len
)
173 : data_sink(std::make_unique
<http_content_length_data_sink_impl
>(out
, len
))
178 output_stream
<char> make_http_content_length_output_stream(output_stream
<char>& out
, size_t& len
) {
179 output_stream_options opts
;
180 opts
.trim_to_size
= true;
181 return output_stream
<char>(http_content_length_data_sink(out
, len
), default_body_sink_buffer_size
, opts
);