Boost Asio and Beast mulitpart/form-data save binary from streambuf












1















My results look like...



POST /post HTTP/1.1
Host: localhost:3003
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0)
Gecko/20100101 Firefox/62.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost:3003/profile
Content-type: multipart/form-data
Content-Length: 14708
Cookie: mycookie=7bdbed41954cd4133a172acb92988e58
Connection: keep-alive

-----------------------------4636945214860352321751082034
...
binary characters...
...
-----------------------------4636945214860352321751082034


Acquired from



boost::asio::async_read(
socket_,
strmbuffer_,
boost::asio::transfer_exactly(bytes_to_transfer),
strand_.wrap(
[this, self](boost::system::error_code ec, std::size_t bytes_transferred)
{
std::stringstream ss;
ss << buffer_data; // from socket_.async_read_some()
ss << &strmbuffer_; // now stringstream contains everything

// the character routine which writes the above
// and which i use for output...

std::string output_file = "../../upload/test.png";
std::ofstream outfile(output_file);
char c;
unsigned bl = boundary.length();
bool endfile = false;
unsigned bufsize = 512;
if(outfile){
char buffer[bufsize];
while(!endfile){
// here looks like below
// to find and pass the first boundary
} // then stream continues...
while(!endfile){
unsigned j = 0;
unsigned k;
memset(buffer, 0, bufsize); // redundant
while(j < bufsize && ss.get(c) && !endfile){
buffer[j] = c;
k = 0;
while(boundary[bl - 1 - k] == buffer[j - k]){
if(k >= bl - 1){
endfile = true;
break;
}
k++;
}
j++;
}
outfile.write(buffer, j);
j = 0;
}
}
}
);


...essentially. Thus, the receiving



socket_.async_read_some()


gives me a



boost::array<char, 8192> buffer_;


which gives me the http request info. But in the case of multipart/form-data, it reads passed the first boundary, which means the next read() doesn't see it. Argh! (So does async_read_until().) So, in



boost::asio::async_read()


i convert the



boost::asio::streambuf strmbuffer_;


to a stringstream and add them together for the std::cout results above.



I'm not convinced that I should be using stringstream, at all. But the above routine (using stringstream) works fine in Boost::Beast. It does NOT in Asio. Unfortunately, the string_body type, which receives the http request in Beast, has a restrictive size limit, 1 meg, i believe. No idea how to change that.



I can't find much on this topic, anywhere. Perhaps, the information is too dangerous. If they told me, they'd have to kill me. What should I be using in Asio to write binary data to disk??










share|improve this question























  • one possibility for the problem - i've noticed that some functions, such as while() loops, are not being called in a serial manner. The output (cout) is not as clean as i have it, above.

    – forresttales
    Nov 24 '18 at 9:12
















1















My results look like...



POST /post HTTP/1.1
Host: localhost:3003
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0)
Gecko/20100101 Firefox/62.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost:3003/profile
Content-type: multipart/form-data
Content-Length: 14708
Cookie: mycookie=7bdbed41954cd4133a172acb92988e58
Connection: keep-alive

-----------------------------4636945214860352321751082034
...
binary characters...
...
-----------------------------4636945214860352321751082034


Acquired from



boost::asio::async_read(
socket_,
strmbuffer_,
boost::asio::transfer_exactly(bytes_to_transfer),
strand_.wrap(
[this, self](boost::system::error_code ec, std::size_t bytes_transferred)
{
std::stringstream ss;
ss << buffer_data; // from socket_.async_read_some()
ss << &strmbuffer_; // now stringstream contains everything

// the character routine which writes the above
// and which i use for output...

std::string output_file = "../../upload/test.png";
std::ofstream outfile(output_file);
char c;
unsigned bl = boundary.length();
bool endfile = false;
unsigned bufsize = 512;
if(outfile){
char buffer[bufsize];
while(!endfile){
// here looks like below
// to find and pass the first boundary
} // then stream continues...
while(!endfile){
unsigned j = 0;
unsigned k;
memset(buffer, 0, bufsize); // redundant
while(j < bufsize && ss.get(c) && !endfile){
buffer[j] = c;
k = 0;
while(boundary[bl - 1 - k] == buffer[j - k]){
if(k >= bl - 1){
endfile = true;
break;
}
k++;
}
j++;
}
outfile.write(buffer, j);
j = 0;
}
}
}
);


...essentially. Thus, the receiving



socket_.async_read_some()


gives me a



boost::array<char, 8192> buffer_;


which gives me the http request info. But in the case of multipart/form-data, it reads passed the first boundary, which means the next read() doesn't see it. Argh! (So does async_read_until().) So, in



boost::asio::async_read()


i convert the



boost::asio::streambuf strmbuffer_;


to a stringstream and add them together for the std::cout results above.



I'm not convinced that I should be using stringstream, at all. But the above routine (using stringstream) works fine in Boost::Beast. It does NOT in Asio. Unfortunately, the string_body type, which receives the http request in Beast, has a restrictive size limit, 1 meg, i believe. No idea how to change that.



I can't find much on this topic, anywhere. Perhaps, the information is too dangerous. If they told me, they'd have to kill me. What should I be using in Asio to write binary data to disk??










share|improve this question























  • one possibility for the problem - i've noticed that some functions, such as while() loops, are not being called in a serial manner. The output (cout) is not as clean as i have it, above.

    – forresttales
    Nov 24 '18 at 9:12














1












1








1








My results look like...



POST /post HTTP/1.1
Host: localhost:3003
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0)
Gecko/20100101 Firefox/62.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost:3003/profile
Content-type: multipart/form-data
Content-Length: 14708
Cookie: mycookie=7bdbed41954cd4133a172acb92988e58
Connection: keep-alive

-----------------------------4636945214860352321751082034
...
binary characters...
...
-----------------------------4636945214860352321751082034


Acquired from



boost::asio::async_read(
socket_,
strmbuffer_,
boost::asio::transfer_exactly(bytes_to_transfer),
strand_.wrap(
[this, self](boost::system::error_code ec, std::size_t bytes_transferred)
{
std::stringstream ss;
ss << buffer_data; // from socket_.async_read_some()
ss << &strmbuffer_; // now stringstream contains everything

// the character routine which writes the above
// and which i use for output...

std::string output_file = "../../upload/test.png";
std::ofstream outfile(output_file);
char c;
unsigned bl = boundary.length();
bool endfile = false;
unsigned bufsize = 512;
if(outfile){
char buffer[bufsize];
while(!endfile){
// here looks like below
// to find and pass the first boundary
} // then stream continues...
while(!endfile){
unsigned j = 0;
unsigned k;
memset(buffer, 0, bufsize); // redundant
while(j < bufsize && ss.get(c) && !endfile){
buffer[j] = c;
k = 0;
while(boundary[bl - 1 - k] == buffer[j - k]){
if(k >= bl - 1){
endfile = true;
break;
}
k++;
}
j++;
}
outfile.write(buffer, j);
j = 0;
}
}
}
);


...essentially. Thus, the receiving



socket_.async_read_some()


gives me a



boost::array<char, 8192> buffer_;


which gives me the http request info. But in the case of multipart/form-data, it reads passed the first boundary, which means the next read() doesn't see it. Argh! (So does async_read_until().) So, in



boost::asio::async_read()


i convert the



boost::asio::streambuf strmbuffer_;


to a stringstream and add them together for the std::cout results above.



I'm not convinced that I should be using stringstream, at all. But the above routine (using stringstream) works fine in Boost::Beast. It does NOT in Asio. Unfortunately, the string_body type, which receives the http request in Beast, has a restrictive size limit, 1 meg, i believe. No idea how to change that.



I can't find much on this topic, anywhere. Perhaps, the information is too dangerous. If they told me, they'd have to kill me. What should I be using in Asio to write binary data to disk??










share|improve this question














My results look like...



POST /post HTTP/1.1
Host: localhost:3003
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0)
Gecko/20100101 Firefox/62.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost:3003/profile
Content-type: multipart/form-data
Content-Length: 14708
Cookie: mycookie=7bdbed41954cd4133a172acb92988e58
Connection: keep-alive

-----------------------------4636945214860352321751082034
...
binary characters...
...
-----------------------------4636945214860352321751082034


Acquired from



boost::asio::async_read(
socket_,
strmbuffer_,
boost::asio::transfer_exactly(bytes_to_transfer),
strand_.wrap(
[this, self](boost::system::error_code ec, std::size_t bytes_transferred)
{
std::stringstream ss;
ss << buffer_data; // from socket_.async_read_some()
ss << &strmbuffer_; // now stringstream contains everything

// the character routine which writes the above
// and which i use for output...

std::string output_file = "../../upload/test.png";
std::ofstream outfile(output_file);
char c;
unsigned bl = boundary.length();
bool endfile = false;
unsigned bufsize = 512;
if(outfile){
char buffer[bufsize];
while(!endfile){
// here looks like below
// to find and pass the first boundary
} // then stream continues...
while(!endfile){
unsigned j = 0;
unsigned k;
memset(buffer, 0, bufsize); // redundant
while(j < bufsize && ss.get(c) && !endfile){
buffer[j] = c;
k = 0;
while(boundary[bl - 1 - k] == buffer[j - k]){
if(k >= bl - 1){
endfile = true;
break;
}
k++;
}
j++;
}
outfile.write(buffer, j);
j = 0;
}
}
}
);


...essentially. Thus, the receiving



socket_.async_read_some()


gives me a



boost::array<char, 8192> buffer_;


which gives me the http request info. But in the case of multipart/form-data, it reads passed the first boundary, which means the next read() doesn't see it. Argh! (So does async_read_until().) So, in



boost::asio::async_read()


i convert the



boost::asio::streambuf strmbuffer_;


to a stringstream and add them together for the std::cout results above.



I'm not convinced that I should be using stringstream, at all. But the above routine (using stringstream) works fine in Boost::Beast. It does NOT in Asio. Unfortunately, the string_body type, which receives the http request in Beast, has a restrictive size limit, 1 meg, i believe. No idea how to change that.



I can't find much on this topic, anywhere. Perhaps, the information is too dangerous. If they told me, they'd have to kill me. What should I be using in Asio to write binary data to disk??







c++ boost-asio boost-beast






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 24 '18 at 7:29









forresttalesforresttales

166




166













  • one possibility for the problem - i've noticed that some functions, such as while() loops, are not being called in a serial manner. The output (cout) is not as clean as i have it, above.

    – forresttales
    Nov 24 '18 at 9:12



















  • one possibility for the problem - i've noticed that some functions, such as while() loops, are not being called in a serial manner. The output (cout) is not as clean as i have it, above.

    – forresttales
    Nov 24 '18 at 9:12

















one possibility for the problem - i've noticed that some functions, such as while() loops, are not being called in a serial manner. The output (cout) is not as clean as i have it, above.

– forresttales
Nov 24 '18 at 9:12





one possibility for the problem - i've noticed that some functions, such as while() loops, are not being called in a serial manner. The output (cout) is not as clean as i have it, above.

– forresttales
Nov 24 '18 at 9:12












2 Answers
2






active

oldest

votes


















1














By default, the HTTP request parser starts with a 1 megabyte limit. This is to prevent resource exhaustion attacks where the client sends very large or endless amounts of body data. You can easily change this limit by calling parser::body_limit with the desired maximum. This is described in the documentation:



https://www.boost.org/doc/libs/1_68_0/libs/beast/doc/html/beast/ref/boost__beast__http__parser/body_limit.html
https://www.boost.org/doc/libs/1_68_0/libs/beast/doc/html/beast/using_http/buffer_oriented_parsing.html



In order to adjust parser parameters such as the body limit (or header limit) you will need to use the "parser stream operation" interface. This is explained here:



https://www.boost.org/doc/libs/1_68_0/libs/beast/doc/html/beast/using_http/parser_stream_operations.html






share|improve this answer



















  • 1





    i really appreciate it. I've looked over the links. In do_read(), I have derived().stream(). That's all i know. If editing in other areas is required, I'm lost. I'm getting into bare-bones networking, now. Libraries are clearly out of my league. The agony is that i'll never understand Boost's networking classes until the day comes when i no longer need them.

    – forresttales
    Nov 27 '18 at 13:28



















0














I will post my own partial solution. It works without error on a 14.2kb png. Above this can result in a segfault, unless the 'magic' number, as the following shows, is adjusted.



I'm using the Boost Asio HTTP Server example C++11.



In connection.hpp, change...



//boost::array<char, 8192> buffer_;
boost::array<char, 512> buffer_;


Also, add...



boost::asio::streambuf strmbuffer_;


In connection.cpp, here is the connection::handle_read()...



void connection::handle_read(const boost::system::error_code& e, std::size_t bytes_transferred)
{
if (!e)
{
//*** buffer_.data() for this file ***
//
//POST /post HTTP/1.1
//Host: localhost:3003
//User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0
//Accept: */*
//Accept-Language: en-US,en;q=0.5
//Accept-Encoding: gzip, deflate
//Referer: http://localhost:3003/profile
//Content-type: multipart/form-data
//Content-Length: 14710
//Cookie: knowstoryadmin=7bdbed41954cd4133a172acb92988e58
//Connection: keep-alive
//
//-----------------------------14071968205478138611648202646
//Content-Disposition: form-data; name="admin_profile_image_load"; filename="tlc-logo.png"
//Content-Type: image/png
//
//�PNG
//▒

std::stringstream strm1;
std::string buffer_data = buffer_.data();
strm1 << buffer_data;

std::string method;
std::smatch match_method;
std::regex regex_method ("\b([^ ]*)( )([^ ]*)( HTTP/1.1)([^ ]*)");

std::string content_type;
std::smatch match_content_type;
std::regex regex_content_type ("\b(Content-type: )([^ ]*)");

std::string line;

while (std::getline(strm1, line)) {
if (std::regex_search(line, match_method, regex_method)) {
method = match_method[0];
method = method.substr(0, method.find(' '));
boost::trim(method);
//std::cout << method << std::endl;
}
if (std::regex_search(line, match_content_type, regex_content_type)) {
content_type = match_content_type[0];
boost::erase_all(content_type, "Content-type:");
boost::trim(content_type);
//std::cout << content_type << std::endl;
}
}

if (method == "POST") {
if (content_type == "multipart/form-data") {

std::string content_length;
std::smatch match_content_length;
std::regex regex_content_length ("\b(Content-Length: )([^ ]*)");

std::string filename;
std::smatch match_filename;
std::regex regex_filename ("\b(filename)([^ ]*)");

std::string action;
std::smatch match_action;
std::regex regex_action ("\b(name)([^ ]*)");

std::string boundary;
std::smatch match_boundary;
std::regex regex_boundary ("([-]{10,}[0-9]{10,})");

std::string line;

strm1.clear();
strm1 << buffer_data;
while (std::getline(strm1, line)) {
if (std::regex_search(line, match_content_length, regex_content_length)) {
//Content-Length: 14710
content_length = match_content_length[0];
boost::erase_all(content_length, "Content-Length:");
boost::trim(content_length);
//std::cout << content_length << std::endl;
}
if (std::regex_search(line, match_filename, regex_filename)) {
filename = match_filename[0];
boost::erase_all(filename, """);
boost::erase_all(action, ";");
boost::erase_all(filename, "filename=");
std::size_t found = filename.find_last_of(".");
std::size_t len = filename.length();
std::string mime = filename.substr(found, len);
boost::trim(filename);
//std::cout << filename << std::endl;
//std::cout << mime << std::endl;
}
if (std::regex_search(line, match_action, regex_action)) {
action = match_action[0];
boost::erase_all(action, """);
boost::erase_all(action, ";");
boost::erase_all(action, "name=");
boost::trim(action);
//std::cout << action << std::endl;
}
if (std::regex_search(line, match_boundary, regex_boundary)) {
boundary = match_boundary[0];
boost::trim(boundary);
//std::cout << boundary << std::endl;
}
}

//pubseekpos works as expected, but useless here
//strmbuffer_.pubseekpos(bytes_transferred);

//content length minus bytes_transfered does NOT yield
//the right result. The number, 392, is the 'magic' number
//adjustment for this file size, approx 14.2kb, that i found
//by trial and error.
//Adjusting the magic number is necessary for every image size
//in order to avoid a segfault.
//bytes_transferred, for each read(), is the only 'reliable'
//number with which to work, as far as i know.
//If there is a brainier way of calculating this,
//i don't care, anymore.
int n_content_length = std::stoi(content_length);
int transfer = n_content_length - bytes_transferred + 392;
auto self(shared_from_this());
boost::asio::async_read(
socket_,
strmbuffer_,
boost::asio::transfer_exactly(transfer),
strand_.wrap(
[this, self, boundary](boost::system::error_code ec, std::size_t bytes_transferred)
{
std::stringstream strm2;
strm2 << &strmbuffer_;
std::string line;
unsigned bufsize = 512;
while (std::getline(strm2, line))
{
if(line.length() == 1){
std::string output_file = "../../upload/test.png";
std::ofstream outfile(output_file);
char c;
unsigned bl = boundary.length();
bool endfile = false;
if(outfile){
char buffer[bufsize];
while(!endfile){
unsigned j = 0;
unsigned k;
while(j < bufsize && strm2.get(c) && !endfile){
buffer[j] = c;
k = 0;
while(boundary[bl - 1 - k] == buffer[j - k]){
if(k >= bl - 1){
endfile = true;
break;
}
k++;
}
j++;
}
outfile.write(buffer, j);
j = 0;
};
outfile.close();
std::cout << "outfile close" << std::endl;
break;
}
}
}
}
)
);
}
else {
// POST AJAX
std::cout << "connection " << method << std::endl;
}
}
else {
boost::tribool result;
boost::tie(result, boost::tuples::ignore) = request_parser_.parse(
request_, buffer_.data(), buffer_.data() + bytes_transferred);

if (result)
{
request_handler_.handle_request(
request_,
reply_);

boost::asio::async_write(
socket_,
reply_.to_buffers(),
strand_.wrap(
boost::bind(
&connection::handle_write,
shared_from_this(),
boost::asio::placeholders::error)
));
}
else if (!result)
{
reply_ = reply::stock_reply(reply::bad_request);
boost::asio::async_write(
socket_,
reply_.to_buffers(),
strand_.wrap(
boost::bind(&connection::handle_write, shared_from_this(),
boost::asio::placeholders::error)));
}
else
{
socket_.async_read_some(
boost::asio::buffer(buffer_),
strand_.wrap(
boost::bind(
&connection::handle_read,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred)));
}
}
}
}


This partial solution is rather 'unobtrusive' for this Asio example. The request_handler class is unchanged and serves GET.



In connection::start(), i tried async_read. But the results were even less predictable. It meant that i had to append two strings, one made from strmbuffer1_, the other from strmbuffer2_, in order to construct (hack) together the stream in the file output loop. No matter how precise my preparations appeared in cout<<, there were roughly 500 or more bytes missing, writing an incomplete image file.



Somewhere in memory there exists, and must exist, the complete information that i need for an uploaded binary and a handle to it. But extracting it from the Asio classes seems unnecessarily tricky.



This is how far I've come with Boost Asio. And this is as far as i go. For what i want, my answers are not here.






share|improve this answer

























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53456123%2fboost-asio-and-beast-mulitpart-form-data-save-binary-from-streambuf%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1














    By default, the HTTP request parser starts with a 1 megabyte limit. This is to prevent resource exhaustion attacks where the client sends very large or endless amounts of body data. You can easily change this limit by calling parser::body_limit with the desired maximum. This is described in the documentation:



    https://www.boost.org/doc/libs/1_68_0/libs/beast/doc/html/beast/ref/boost__beast__http__parser/body_limit.html
    https://www.boost.org/doc/libs/1_68_0/libs/beast/doc/html/beast/using_http/buffer_oriented_parsing.html



    In order to adjust parser parameters such as the body limit (or header limit) you will need to use the "parser stream operation" interface. This is explained here:



    https://www.boost.org/doc/libs/1_68_0/libs/beast/doc/html/beast/using_http/parser_stream_operations.html






    share|improve this answer



















    • 1





      i really appreciate it. I've looked over the links. In do_read(), I have derived().stream(). That's all i know. If editing in other areas is required, I'm lost. I'm getting into bare-bones networking, now. Libraries are clearly out of my league. The agony is that i'll never understand Boost's networking classes until the day comes when i no longer need them.

      – forresttales
      Nov 27 '18 at 13:28
















    1














    By default, the HTTP request parser starts with a 1 megabyte limit. This is to prevent resource exhaustion attacks where the client sends very large or endless amounts of body data. You can easily change this limit by calling parser::body_limit with the desired maximum. This is described in the documentation:



    https://www.boost.org/doc/libs/1_68_0/libs/beast/doc/html/beast/ref/boost__beast__http__parser/body_limit.html
    https://www.boost.org/doc/libs/1_68_0/libs/beast/doc/html/beast/using_http/buffer_oriented_parsing.html



    In order to adjust parser parameters such as the body limit (or header limit) you will need to use the "parser stream operation" interface. This is explained here:



    https://www.boost.org/doc/libs/1_68_0/libs/beast/doc/html/beast/using_http/parser_stream_operations.html






    share|improve this answer



















    • 1





      i really appreciate it. I've looked over the links. In do_read(), I have derived().stream(). That's all i know. If editing in other areas is required, I'm lost. I'm getting into bare-bones networking, now. Libraries are clearly out of my league. The agony is that i'll never understand Boost's networking classes until the day comes when i no longer need them.

      – forresttales
      Nov 27 '18 at 13:28














    1












    1








    1







    By default, the HTTP request parser starts with a 1 megabyte limit. This is to prevent resource exhaustion attacks where the client sends very large or endless amounts of body data. You can easily change this limit by calling parser::body_limit with the desired maximum. This is described in the documentation:



    https://www.boost.org/doc/libs/1_68_0/libs/beast/doc/html/beast/ref/boost__beast__http__parser/body_limit.html
    https://www.boost.org/doc/libs/1_68_0/libs/beast/doc/html/beast/using_http/buffer_oriented_parsing.html



    In order to adjust parser parameters such as the body limit (or header limit) you will need to use the "parser stream operation" interface. This is explained here:



    https://www.boost.org/doc/libs/1_68_0/libs/beast/doc/html/beast/using_http/parser_stream_operations.html






    share|improve this answer













    By default, the HTTP request parser starts with a 1 megabyte limit. This is to prevent resource exhaustion attacks where the client sends very large or endless amounts of body data. You can easily change this limit by calling parser::body_limit with the desired maximum. This is described in the documentation:



    https://www.boost.org/doc/libs/1_68_0/libs/beast/doc/html/beast/ref/boost__beast__http__parser/body_limit.html
    https://www.boost.org/doc/libs/1_68_0/libs/beast/doc/html/beast/using_http/buffer_oriented_parsing.html



    In order to adjust parser parameters such as the body limit (or header limit) you will need to use the "parser stream operation" interface. This is explained here:



    https://www.boost.org/doc/libs/1_68_0/libs/beast/doc/html/beast/using_http/parser_stream_operations.html







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Nov 25 '18 at 13:29









    Vinnie FalcoVinnie Falco

    2,5891432




    2,5891432








    • 1





      i really appreciate it. I've looked over the links. In do_read(), I have derived().stream(). That's all i know. If editing in other areas is required, I'm lost. I'm getting into bare-bones networking, now. Libraries are clearly out of my league. The agony is that i'll never understand Boost's networking classes until the day comes when i no longer need them.

      – forresttales
      Nov 27 '18 at 13:28














    • 1





      i really appreciate it. I've looked over the links. In do_read(), I have derived().stream(). That's all i know. If editing in other areas is required, I'm lost. I'm getting into bare-bones networking, now. Libraries are clearly out of my league. The agony is that i'll never understand Boost's networking classes until the day comes when i no longer need them.

      – forresttales
      Nov 27 '18 at 13:28








    1




    1





    i really appreciate it. I've looked over the links. In do_read(), I have derived().stream(). That's all i know. If editing in other areas is required, I'm lost. I'm getting into bare-bones networking, now. Libraries are clearly out of my league. The agony is that i'll never understand Boost's networking classes until the day comes when i no longer need them.

    – forresttales
    Nov 27 '18 at 13:28





    i really appreciate it. I've looked over the links. In do_read(), I have derived().stream(). That's all i know. If editing in other areas is required, I'm lost. I'm getting into bare-bones networking, now. Libraries are clearly out of my league. The agony is that i'll never understand Boost's networking classes until the day comes when i no longer need them.

    – forresttales
    Nov 27 '18 at 13:28













    0














    I will post my own partial solution. It works without error on a 14.2kb png. Above this can result in a segfault, unless the 'magic' number, as the following shows, is adjusted.



    I'm using the Boost Asio HTTP Server example C++11.



    In connection.hpp, change...



    //boost::array<char, 8192> buffer_;
    boost::array<char, 512> buffer_;


    Also, add...



    boost::asio::streambuf strmbuffer_;


    In connection.cpp, here is the connection::handle_read()...



    void connection::handle_read(const boost::system::error_code& e, std::size_t bytes_transferred)
    {
    if (!e)
    {
    //*** buffer_.data() for this file ***
    //
    //POST /post HTTP/1.1
    //Host: localhost:3003
    //User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0
    //Accept: */*
    //Accept-Language: en-US,en;q=0.5
    //Accept-Encoding: gzip, deflate
    //Referer: http://localhost:3003/profile
    //Content-type: multipart/form-data
    //Content-Length: 14710
    //Cookie: knowstoryadmin=7bdbed41954cd4133a172acb92988e58
    //Connection: keep-alive
    //
    //-----------------------------14071968205478138611648202646
    //Content-Disposition: form-data; name="admin_profile_image_load"; filename="tlc-logo.png"
    //Content-Type: image/png
    //
    //�PNG
    //▒

    std::stringstream strm1;
    std::string buffer_data = buffer_.data();
    strm1 << buffer_data;

    std::string method;
    std::smatch match_method;
    std::regex regex_method ("\b([^ ]*)( )([^ ]*)( HTTP/1.1)([^ ]*)");

    std::string content_type;
    std::smatch match_content_type;
    std::regex regex_content_type ("\b(Content-type: )([^ ]*)");

    std::string line;

    while (std::getline(strm1, line)) {
    if (std::regex_search(line, match_method, regex_method)) {
    method = match_method[0];
    method = method.substr(0, method.find(' '));
    boost::trim(method);
    //std::cout << method << std::endl;
    }
    if (std::regex_search(line, match_content_type, regex_content_type)) {
    content_type = match_content_type[0];
    boost::erase_all(content_type, "Content-type:");
    boost::trim(content_type);
    //std::cout << content_type << std::endl;
    }
    }

    if (method == "POST") {
    if (content_type == "multipart/form-data") {

    std::string content_length;
    std::smatch match_content_length;
    std::regex regex_content_length ("\b(Content-Length: )([^ ]*)");

    std::string filename;
    std::smatch match_filename;
    std::regex regex_filename ("\b(filename)([^ ]*)");

    std::string action;
    std::smatch match_action;
    std::regex regex_action ("\b(name)([^ ]*)");

    std::string boundary;
    std::smatch match_boundary;
    std::regex regex_boundary ("([-]{10,}[0-9]{10,})");

    std::string line;

    strm1.clear();
    strm1 << buffer_data;
    while (std::getline(strm1, line)) {
    if (std::regex_search(line, match_content_length, regex_content_length)) {
    //Content-Length: 14710
    content_length = match_content_length[0];
    boost::erase_all(content_length, "Content-Length:");
    boost::trim(content_length);
    //std::cout << content_length << std::endl;
    }
    if (std::regex_search(line, match_filename, regex_filename)) {
    filename = match_filename[0];
    boost::erase_all(filename, """);
    boost::erase_all(action, ";");
    boost::erase_all(filename, "filename=");
    std::size_t found = filename.find_last_of(".");
    std::size_t len = filename.length();
    std::string mime = filename.substr(found, len);
    boost::trim(filename);
    //std::cout << filename << std::endl;
    //std::cout << mime << std::endl;
    }
    if (std::regex_search(line, match_action, regex_action)) {
    action = match_action[0];
    boost::erase_all(action, """);
    boost::erase_all(action, ";");
    boost::erase_all(action, "name=");
    boost::trim(action);
    //std::cout << action << std::endl;
    }
    if (std::regex_search(line, match_boundary, regex_boundary)) {
    boundary = match_boundary[0];
    boost::trim(boundary);
    //std::cout << boundary << std::endl;
    }
    }

    //pubseekpos works as expected, but useless here
    //strmbuffer_.pubseekpos(bytes_transferred);

    //content length minus bytes_transfered does NOT yield
    //the right result. The number, 392, is the 'magic' number
    //adjustment for this file size, approx 14.2kb, that i found
    //by trial and error.
    //Adjusting the magic number is necessary for every image size
    //in order to avoid a segfault.
    //bytes_transferred, for each read(), is the only 'reliable'
    //number with which to work, as far as i know.
    //If there is a brainier way of calculating this,
    //i don't care, anymore.
    int n_content_length = std::stoi(content_length);
    int transfer = n_content_length - bytes_transferred + 392;
    auto self(shared_from_this());
    boost::asio::async_read(
    socket_,
    strmbuffer_,
    boost::asio::transfer_exactly(transfer),
    strand_.wrap(
    [this, self, boundary](boost::system::error_code ec, std::size_t bytes_transferred)
    {
    std::stringstream strm2;
    strm2 << &strmbuffer_;
    std::string line;
    unsigned bufsize = 512;
    while (std::getline(strm2, line))
    {
    if(line.length() == 1){
    std::string output_file = "../../upload/test.png";
    std::ofstream outfile(output_file);
    char c;
    unsigned bl = boundary.length();
    bool endfile = false;
    if(outfile){
    char buffer[bufsize];
    while(!endfile){
    unsigned j = 0;
    unsigned k;
    while(j < bufsize && strm2.get(c) && !endfile){
    buffer[j] = c;
    k = 0;
    while(boundary[bl - 1 - k] == buffer[j - k]){
    if(k >= bl - 1){
    endfile = true;
    break;
    }
    k++;
    }
    j++;
    }
    outfile.write(buffer, j);
    j = 0;
    };
    outfile.close();
    std::cout << "outfile close" << std::endl;
    break;
    }
    }
    }
    }
    )
    );
    }
    else {
    // POST AJAX
    std::cout << "connection " << method << std::endl;
    }
    }
    else {
    boost::tribool result;
    boost::tie(result, boost::tuples::ignore) = request_parser_.parse(
    request_, buffer_.data(), buffer_.data() + bytes_transferred);

    if (result)
    {
    request_handler_.handle_request(
    request_,
    reply_);

    boost::asio::async_write(
    socket_,
    reply_.to_buffers(),
    strand_.wrap(
    boost::bind(
    &connection::handle_write,
    shared_from_this(),
    boost::asio::placeholders::error)
    ));
    }
    else if (!result)
    {
    reply_ = reply::stock_reply(reply::bad_request);
    boost::asio::async_write(
    socket_,
    reply_.to_buffers(),
    strand_.wrap(
    boost::bind(&connection::handle_write, shared_from_this(),
    boost::asio::placeholders::error)));
    }
    else
    {
    socket_.async_read_some(
    boost::asio::buffer(buffer_),
    strand_.wrap(
    boost::bind(
    &connection::handle_read,
    shared_from_this(),
    boost::asio::placeholders::error,
    boost::asio::placeholders::bytes_transferred)));
    }
    }
    }
    }


    This partial solution is rather 'unobtrusive' for this Asio example. The request_handler class is unchanged and serves GET.



    In connection::start(), i tried async_read. But the results were even less predictable. It meant that i had to append two strings, one made from strmbuffer1_, the other from strmbuffer2_, in order to construct (hack) together the stream in the file output loop. No matter how precise my preparations appeared in cout<<, there were roughly 500 or more bytes missing, writing an incomplete image file.



    Somewhere in memory there exists, and must exist, the complete information that i need for an uploaded binary and a handle to it. But extracting it from the Asio classes seems unnecessarily tricky.



    This is how far I've come with Boost Asio. And this is as far as i go. For what i want, my answers are not here.






    share|improve this answer






























      0














      I will post my own partial solution. It works without error on a 14.2kb png. Above this can result in a segfault, unless the 'magic' number, as the following shows, is adjusted.



      I'm using the Boost Asio HTTP Server example C++11.



      In connection.hpp, change...



      //boost::array<char, 8192> buffer_;
      boost::array<char, 512> buffer_;


      Also, add...



      boost::asio::streambuf strmbuffer_;


      In connection.cpp, here is the connection::handle_read()...



      void connection::handle_read(const boost::system::error_code& e, std::size_t bytes_transferred)
      {
      if (!e)
      {
      //*** buffer_.data() for this file ***
      //
      //POST /post HTTP/1.1
      //Host: localhost:3003
      //User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0
      //Accept: */*
      //Accept-Language: en-US,en;q=0.5
      //Accept-Encoding: gzip, deflate
      //Referer: http://localhost:3003/profile
      //Content-type: multipart/form-data
      //Content-Length: 14710
      //Cookie: knowstoryadmin=7bdbed41954cd4133a172acb92988e58
      //Connection: keep-alive
      //
      //-----------------------------14071968205478138611648202646
      //Content-Disposition: form-data; name="admin_profile_image_load"; filename="tlc-logo.png"
      //Content-Type: image/png
      //
      //�PNG
      //▒

      std::stringstream strm1;
      std::string buffer_data = buffer_.data();
      strm1 << buffer_data;

      std::string method;
      std::smatch match_method;
      std::regex regex_method ("\b([^ ]*)( )([^ ]*)( HTTP/1.1)([^ ]*)");

      std::string content_type;
      std::smatch match_content_type;
      std::regex regex_content_type ("\b(Content-type: )([^ ]*)");

      std::string line;

      while (std::getline(strm1, line)) {
      if (std::regex_search(line, match_method, regex_method)) {
      method = match_method[0];
      method = method.substr(0, method.find(' '));
      boost::trim(method);
      //std::cout << method << std::endl;
      }
      if (std::regex_search(line, match_content_type, regex_content_type)) {
      content_type = match_content_type[0];
      boost::erase_all(content_type, "Content-type:");
      boost::trim(content_type);
      //std::cout << content_type << std::endl;
      }
      }

      if (method == "POST") {
      if (content_type == "multipart/form-data") {

      std::string content_length;
      std::smatch match_content_length;
      std::regex regex_content_length ("\b(Content-Length: )([^ ]*)");

      std::string filename;
      std::smatch match_filename;
      std::regex regex_filename ("\b(filename)([^ ]*)");

      std::string action;
      std::smatch match_action;
      std::regex regex_action ("\b(name)([^ ]*)");

      std::string boundary;
      std::smatch match_boundary;
      std::regex regex_boundary ("([-]{10,}[0-9]{10,})");

      std::string line;

      strm1.clear();
      strm1 << buffer_data;
      while (std::getline(strm1, line)) {
      if (std::regex_search(line, match_content_length, regex_content_length)) {
      //Content-Length: 14710
      content_length = match_content_length[0];
      boost::erase_all(content_length, "Content-Length:");
      boost::trim(content_length);
      //std::cout << content_length << std::endl;
      }
      if (std::regex_search(line, match_filename, regex_filename)) {
      filename = match_filename[0];
      boost::erase_all(filename, """);
      boost::erase_all(action, ";");
      boost::erase_all(filename, "filename=");
      std::size_t found = filename.find_last_of(".");
      std::size_t len = filename.length();
      std::string mime = filename.substr(found, len);
      boost::trim(filename);
      //std::cout << filename << std::endl;
      //std::cout << mime << std::endl;
      }
      if (std::regex_search(line, match_action, regex_action)) {
      action = match_action[0];
      boost::erase_all(action, """);
      boost::erase_all(action, ";");
      boost::erase_all(action, "name=");
      boost::trim(action);
      //std::cout << action << std::endl;
      }
      if (std::regex_search(line, match_boundary, regex_boundary)) {
      boundary = match_boundary[0];
      boost::trim(boundary);
      //std::cout << boundary << std::endl;
      }
      }

      //pubseekpos works as expected, but useless here
      //strmbuffer_.pubseekpos(bytes_transferred);

      //content length minus bytes_transfered does NOT yield
      //the right result. The number, 392, is the 'magic' number
      //adjustment for this file size, approx 14.2kb, that i found
      //by trial and error.
      //Adjusting the magic number is necessary for every image size
      //in order to avoid a segfault.
      //bytes_transferred, for each read(), is the only 'reliable'
      //number with which to work, as far as i know.
      //If there is a brainier way of calculating this,
      //i don't care, anymore.
      int n_content_length = std::stoi(content_length);
      int transfer = n_content_length - bytes_transferred + 392;
      auto self(shared_from_this());
      boost::asio::async_read(
      socket_,
      strmbuffer_,
      boost::asio::transfer_exactly(transfer),
      strand_.wrap(
      [this, self, boundary](boost::system::error_code ec, std::size_t bytes_transferred)
      {
      std::stringstream strm2;
      strm2 << &strmbuffer_;
      std::string line;
      unsigned bufsize = 512;
      while (std::getline(strm2, line))
      {
      if(line.length() == 1){
      std::string output_file = "../../upload/test.png";
      std::ofstream outfile(output_file);
      char c;
      unsigned bl = boundary.length();
      bool endfile = false;
      if(outfile){
      char buffer[bufsize];
      while(!endfile){
      unsigned j = 0;
      unsigned k;
      while(j < bufsize && strm2.get(c) && !endfile){
      buffer[j] = c;
      k = 0;
      while(boundary[bl - 1 - k] == buffer[j - k]){
      if(k >= bl - 1){
      endfile = true;
      break;
      }
      k++;
      }
      j++;
      }
      outfile.write(buffer, j);
      j = 0;
      };
      outfile.close();
      std::cout << "outfile close" << std::endl;
      break;
      }
      }
      }
      }
      )
      );
      }
      else {
      // POST AJAX
      std::cout << "connection " << method << std::endl;
      }
      }
      else {
      boost::tribool result;
      boost::tie(result, boost::tuples::ignore) = request_parser_.parse(
      request_, buffer_.data(), buffer_.data() + bytes_transferred);

      if (result)
      {
      request_handler_.handle_request(
      request_,
      reply_);

      boost::asio::async_write(
      socket_,
      reply_.to_buffers(),
      strand_.wrap(
      boost::bind(
      &connection::handle_write,
      shared_from_this(),
      boost::asio::placeholders::error)
      ));
      }
      else if (!result)
      {
      reply_ = reply::stock_reply(reply::bad_request);
      boost::asio::async_write(
      socket_,
      reply_.to_buffers(),
      strand_.wrap(
      boost::bind(&connection::handle_write, shared_from_this(),
      boost::asio::placeholders::error)));
      }
      else
      {
      socket_.async_read_some(
      boost::asio::buffer(buffer_),
      strand_.wrap(
      boost::bind(
      &connection::handle_read,
      shared_from_this(),
      boost::asio::placeholders::error,
      boost::asio::placeholders::bytes_transferred)));
      }
      }
      }
      }


      This partial solution is rather 'unobtrusive' for this Asio example. The request_handler class is unchanged and serves GET.



      In connection::start(), i tried async_read. But the results were even less predictable. It meant that i had to append two strings, one made from strmbuffer1_, the other from strmbuffer2_, in order to construct (hack) together the stream in the file output loop. No matter how precise my preparations appeared in cout<<, there were roughly 500 or more bytes missing, writing an incomplete image file.



      Somewhere in memory there exists, and must exist, the complete information that i need for an uploaded binary and a handle to it. But extracting it from the Asio classes seems unnecessarily tricky.



      This is how far I've come with Boost Asio. And this is as far as i go. For what i want, my answers are not here.






      share|improve this answer




























        0












        0








        0







        I will post my own partial solution. It works without error on a 14.2kb png. Above this can result in a segfault, unless the 'magic' number, as the following shows, is adjusted.



        I'm using the Boost Asio HTTP Server example C++11.



        In connection.hpp, change...



        //boost::array<char, 8192> buffer_;
        boost::array<char, 512> buffer_;


        Also, add...



        boost::asio::streambuf strmbuffer_;


        In connection.cpp, here is the connection::handle_read()...



        void connection::handle_read(const boost::system::error_code& e, std::size_t bytes_transferred)
        {
        if (!e)
        {
        //*** buffer_.data() for this file ***
        //
        //POST /post HTTP/1.1
        //Host: localhost:3003
        //User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0
        //Accept: */*
        //Accept-Language: en-US,en;q=0.5
        //Accept-Encoding: gzip, deflate
        //Referer: http://localhost:3003/profile
        //Content-type: multipart/form-data
        //Content-Length: 14710
        //Cookie: knowstoryadmin=7bdbed41954cd4133a172acb92988e58
        //Connection: keep-alive
        //
        //-----------------------------14071968205478138611648202646
        //Content-Disposition: form-data; name="admin_profile_image_load"; filename="tlc-logo.png"
        //Content-Type: image/png
        //
        //�PNG
        //▒

        std::stringstream strm1;
        std::string buffer_data = buffer_.data();
        strm1 << buffer_data;

        std::string method;
        std::smatch match_method;
        std::regex regex_method ("\b([^ ]*)( )([^ ]*)( HTTP/1.1)([^ ]*)");

        std::string content_type;
        std::smatch match_content_type;
        std::regex regex_content_type ("\b(Content-type: )([^ ]*)");

        std::string line;

        while (std::getline(strm1, line)) {
        if (std::regex_search(line, match_method, regex_method)) {
        method = match_method[0];
        method = method.substr(0, method.find(' '));
        boost::trim(method);
        //std::cout << method << std::endl;
        }
        if (std::regex_search(line, match_content_type, regex_content_type)) {
        content_type = match_content_type[0];
        boost::erase_all(content_type, "Content-type:");
        boost::trim(content_type);
        //std::cout << content_type << std::endl;
        }
        }

        if (method == "POST") {
        if (content_type == "multipart/form-data") {

        std::string content_length;
        std::smatch match_content_length;
        std::regex regex_content_length ("\b(Content-Length: )([^ ]*)");

        std::string filename;
        std::smatch match_filename;
        std::regex regex_filename ("\b(filename)([^ ]*)");

        std::string action;
        std::smatch match_action;
        std::regex regex_action ("\b(name)([^ ]*)");

        std::string boundary;
        std::smatch match_boundary;
        std::regex regex_boundary ("([-]{10,}[0-9]{10,})");

        std::string line;

        strm1.clear();
        strm1 << buffer_data;
        while (std::getline(strm1, line)) {
        if (std::regex_search(line, match_content_length, regex_content_length)) {
        //Content-Length: 14710
        content_length = match_content_length[0];
        boost::erase_all(content_length, "Content-Length:");
        boost::trim(content_length);
        //std::cout << content_length << std::endl;
        }
        if (std::regex_search(line, match_filename, regex_filename)) {
        filename = match_filename[0];
        boost::erase_all(filename, """);
        boost::erase_all(action, ";");
        boost::erase_all(filename, "filename=");
        std::size_t found = filename.find_last_of(".");
        std::size_t len = filename.length();
        std::string mime = filename.substr(found, len);
        boost::trim(filename);
        //std::cout << filename << std::endl;
        //std::cout << mime << std::endl;
        }
        if (std::regex_search(line, match_action, regex_action)) {
        action = match_action[0];
        boost::erase_all(action, """);
        boost::erase_all(action, ";");
        boost::erase_all(action, "name=");
        boost::trim(action);
        //std::cout << action << std::endl;
        }
        if (std::regex_search(line, match_boundary, regex_boundary)) {
        boundary = match_boundary[0];
        boost::trim(boundary);
        //std::cout << boundary << std::endl;
        }
        }

        //pubseekpos works as expected, but useless here
        //strmbuffer_.pubseekpos(bytes_transferred);

        //content length minus bytes_transfered does NOT yield
        //the right result. The number, 392, is the 'magic' number
        //adjustment for this file size, approx 14.2kb, that i found
        //by trial and error.
        //Adjusting the magic number is necessary for every image size
        //in order to avoid a segfault.
        //bytes_transferred, for each read(), is the only 'reliable'
        //number with which to work, as far as i know.
        //If there is a brainier way of calculating this,
        //i don't care, anymore.
        int n_content_length = std::stoi(content_length);
        int transfer = n_content_length - bytes_transferred + 392;
        auto self(shared_from_this());
        boost::asio::async_read(
        socket_,
        strmbuffer_,
        boost::asio::transfer_exactly(transfer),
        strand_.wrap(
        [this, self, boundary](boost::system::error_code ec, std::size_t bytes_transferred)
        {
        std::stringstream strm2;
        strm2 << &strmbuffer_;
        std::string line;
        unsigned bufsize = 512;
        while (std::getline(strm2, line))
        {
        if(line.length() == 1){
        std::string output_file = "../../upload/test.png";
        std::ofstream outfile(output_file);
        char c;
        unsigned bl = boundary.length();
        bool endfile = false;
        if(outfile){
        char buffer[bufsize];
        while(!endfile){
        unsigned j = 0;
        unsigned k;
        while(j < bufsize && strm2.get(c) && !endfile){
        buffer[j] = c;
        k = 0;
        while(boundary[bl - 1 - k] == buffer[j - k]){
        if(k >= bl - 1){
        endfile = true;
        break;
        }
        k++;
        }
        j++;
        }
        outfile.write(buffer, j);
        j = 0;
        };
        outfile.close();
        std::cout << "outfile close" << std::endl;
        break;
        }
        }
        }
        }
        )
        );
        }
        else {
        // POST AJAX
        std::cout << "connection " << method << std::endl;
        }
        }
        else {
        boost::tribool result;
        boost::tie(result, boost::tuples::ignore) = request_parser_.parse(
        request_, buffer_.data(), buffer_.data() + bytes_transferred);

        if (result)
        {
        request_handler_.handle_request(
        request_,
        reply_);

        boost::asio::async_write(
        socket_,
        reply_.to_buffers(),
        strand_.wrap(
        boost::bind(
        &connection::handle_write,
        shared_from_this(),
        boost::asio::placeholders::error)
        ));
        }
        else if (!result)
        {
        reply_ = reply::stock_reply(reply::bad_request);
        boost::asio::async_write(
        socket_,
        reply_.to_buffers(),
        strand_.wrap(
        boost::bind(&connection::handle_write, shared_from_this(),
        boost::asio::placeholders::error)));
        }
        else
        {
        socket_.async_read_some(
        boost::asio::buffer(buffer_),
        strand_.wrap(
        boost::bind(
        &connection::handle_read,
        shared_from_this(),
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred)));
        }
        }
        }
        }


        This partial solution is rather 'unobtrusive' for this Asio example. The request_handler class is unchanged and serves GET.



        In connection::start(), i tried async_read. But the results were even less predictable. It meant that i had to append two strings, one made from strmbuffer1_, the other from strmbuffer2_, in order to construct (hack) together the stream in the file output loop. No matter how precise my preparations appeared in cout<<, there were roughly 500 or more bytes missing, writing an incomplete image file.



        Somewhere in memory there exists, and must exist, the complete information that i need for an uploaded binary and a handle to it. But extracting it from the Asio classes seems unnecessarily tricky.



        This is how far I've come with Boost Asio. And this is as far as i go. For what i want, my answers are not here.






        share|improve this answer















        I will post my own partial solution. It works without error on a 14.2kb png. Above this can result in a segfault, unless the 'magic' number, as the following shows, is adjusted.



        I'm using the Boost Asio HTTP Server example C++11.



        In connection.hpp, change...



        //boost::array<char, 8192> buffer_;
        boost::array<char, 512> buffer_;


        Also, add...



        boost::asio::streambuf strmbuffer_;


        In connection.cpp, here is the connection::handle_read()...



        void connection::handle_read(const boost::system::error_code& e, std::size_t bytes_transferred)
        {
        if (!e)
        {
        //*** buffer_.data() for this file ***
        //
        //POST /post HTTP/1.1
        //Host: localhost:3003
        //User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0
        //Accept: */*
        //Accept-Language: en-US,en;q=0.5
        //Accept-Encoding: gzip, deflate
        //Referer: http://localhost:3003/profile
        //Content-type: multipart/form-data
        //Content-Length: 14710
        //Cookie: knowstoryadmin=7bdbed41954cd4133a172acb92988e58
        //Connection: keep-alive
        //
        //-----------------------------14071968205478138611648202646
        //Content-Disposition: form-data; name="admin_profile_image_load"; filename="tlc-logo.png"
        //Content-Type: image/png
        //
        //�PNG
        //▒

        std::stringstream strm1;
        std::string buffer_data = buffer_.data();
        strm1 << buffer_data;

        std::string method;
        std::smatch match_method;
        std::regex regex_method ("\b([^ ]*)( )([^ ]*)( HTTP/1.1)([^ ]*)");

        std::string content_type;
        std::smatch match_content_type;
        std::regex regex_content_type ("\b(Content-type: )([^ ]*)");

        std::string line;

        while (std::getline(strm1, line)) {
        if (std::regex_search(line, match_method, regex_method)) {
        method = match_method[0];
        method = method.substr(0, method.find(' '));
        boost::trim(method);
        //std::cout << method << std::endl;
        }
        if (std::regex_search(line, match_content_type, regex_content_type)) {
        content_type = match_content_type[0];
        boost::erase_all(content_type, "Content-type:");
        boost::trim(content_type);
        //std::cout << content_type << std::endl;
        }
        }

        if (method == "POST") {
        if (content_type == "multipart/form-data") {

        std::string content_length;
        std::smatch match_content_length;
        std::regex regex_content_length ("\b(Content-Length: )([^ ]*)");

        std::string filename;
        std::smatch match_filename;
        std::regex regex_filename ("\b(filename)([^ ]*)");

        std::string action;
        std::smatch match_action;
        std::regex regex_action ("\b(name)([^ ]*)");

        std::string boundary;
        std::smatch match_boundary;
        std::regex regex_boundary ("([-]{10,}[0-9]{10,})");

        std::string line;

        strm1.clear();
        strm1 << buffer_data;
        while (std::getline(strm1, line)) {
        if (std::regex_search(line, match_content_length, regex_content_length)) {
        //Content-Length: 14710
        content_length = match_content_length[0];
        boost::erase_all(content_length, "Content-Length:");
        boost::trim(content_length);
        //std::cout << content_length << std::endl;
        }
        if (std::regex_search(line, match_filename, regex_filename)) {
        filename = match_filename[0];
        boost::erase_all(filename, """);
        boost::erase_all(action, ";");
        boost::erase_all(filename, "filename=");
        std::size_t found = filename.find_last_of(".");
        std::size_t len = filename.length();
        std::string mime = filename.substr(found, len);
        boost::trim(filename);
        //std::cout << filename << std::endl;
        //std::cout << mime << std::endl;
        }
        if (std::regex_search(line, match_action, regex_action)) {
        action = match_action[0];
        boost::erase_all(action, """);
        boost::erase_all(action, ";");
        boost::erase_all(action, "name=");
        boost::trim(action);
        //std::cout << action << std::endl;
        }
        if (std::regex_search(line, match_boundary, regex_boundary)) {
        boundary = match_boundary[0];
        boost::trim(boundary);
        //std::cout << boundary << std::endl;
        }
        }

        //pubseekpos works as expected, but useless here
        //strmbuffer_.pubseekpos(bytes_transferred);

        //content length minus bytes_transfered does NOT yield
        //the right result. The number, 392, is the 'magic' number
        //adjustment for this file size, approx 14.2kb, that i found
        //by trial and error.
        //Adjusting the magic number is necessary for every image size
        //in order to avoid a segfault.
        //bytes_transferred, for each read(), is the only 'reliable'
        //number with which to work, as far as i know.
        //If there is a brainier way of calculating this,
        //i don't care, anymore.
        int n_content_length = std::stoi(content_length);
        int transfer = n_content_length - bytes_transferred + 392;
        auto self(shared_from_this());
        boost::asio::async_read(
        socket_,
        strmbuffer_,
        boost::asio::transfer_exactly(transfer),
        strand_.wrap(
        [this, self, boundary](boost::system::error_code ec, std::size_t bytes_transferred)
        {
        std::stringstream strm2;
        strm2 << &strmbuffer_;
        std::string line;
        unsigned bufsize = 512;
        while (std::getline(strm2, line))
        {
        if(line.length() == 1){
        std::string output_file = "../../upload/test.png";
        std::ofstream outfile(output_file);
        char c;
        unsigned bl = boundary.length();
        bool endfile = false;
        if(outfile){
        char buffer[bufsize];
        while(!endfile){
        unsigned j = 0;
        unsigned k;
        while(j < bufsize && strm2.get(c) && !endfile){
        buffer[j] = c;
        k = 0;
        while(boundary[bl - 1 - k] == buffer[j - k]){
        if(k >= bl - 1){
        endfile = true;
        break;
        }
        k++;
        }
        j++;
        }
        outfile.write(buffer, j);
        j = 0;
        };
        outfile.close();
        std::cout << "outfile close" << std::endl;
        break;
        }
        }
        }
        }
        )
        );
        }
        else {
        // POST AJAX
        std::cout << "connection " << method << std::endl;
        }
        }
        else {
        boost::tribool result;
        boost::tie(result, boost::tuples::ignore) = request_parser_.parse(
        request_, buffer_.data(), buffer_.data() + bytes_transferred);

        if (result)
        {
        request_handler_.handle_request(
        request_,
        reply_);

        boost::asio::async_write(
        socket_,
        reply_.to_buffers(),
        strand_.wrap(
        boost::bind(
        &connection::handle_write,
        shared_from_this(),
        boost::asio::placeholders::error)
        ));
        }
        else if (!result)
        {
        reply_ = reply::stock_reply(reply::bad_request);
        boost::asio::async_write(
        socket_,
        reply_.to_buffers(),
        strand_.wrap(
        boost::bind(&connection::handle_write, shared_from_this(),
        boost::asio::placeholders::error)));
        }
        else
        {
        socket_.async_read_some(
        boost::asio::buffer(buffer_),
        strand_.wrap(
        boost::bind(
        &connection::handle_read,
        shared_from_this(),
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred)));
        }
        }
        }
        }


        This partial solution is rather 'unobtrusive' for this Asio example. The request_handler class is unchanged and serves GET.



        In connection::start(), i tried async_read. But the results were even less predictable. It meant that i had to append two strings, one made from strmbuffer1_, the other from strmbuffer2_, in order to construct (hack) together the stream in the file output loop. No matter how precise my preparations appeared in cout<<, there were roughly 500 or more bytes missing, writing an incomplete image file.



        Somewhere in memory there exists, and must exist, the complete information that i need for an uploaded binary and a handle to it. But extracting it from the Asio classes seems unnecessarily tricky.



        This is how far I've come with Boost Asio. And this is as far as i go. For what i want, my answers are not here.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 27 '18 at 13:33

























        answered Nov 27 '18 at 12:59









        forresttalesforresttales

        166




        166






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53456123%2fboost-asio-and-beast-mulitpart-form-data-save-binary-from-streambuf%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks

            Calculate evaluation metrics using cross_val_predict sklearn

            Insert data from modal to MySQL (multiple modal on website)