From: David H. <dav...@gm...> - 2011-01-28 01:35:22
|
Hi everybody, I'm working with a HTTP Synchronous server and am experimenting with a file server and different mime types. My question is that I have been using stock_reply for the response and am wondering if there are other methods for sending messages. The line I have been using is: "response = Server::response::stock_reply(ok, msg);" In all the examples I see and also looking through the code all I can find is the "stock_reply" method. Any ideas? Thanks, -Dave- |
From: Dean M. B. <mik...@gm...> - 2011-01-28 04:46:26
|
On Fri, Jan 28, 2011 at 9:35 AM, David Hite <dav...@gm...> wrote: > My question is that I have been using stock_reply for the response and am > wondering if there are other methods for sending messages. > > The line I have been using is: > "response = Server::response::stock_reply(ok, msg);" > In all the examples I see and also looking through the code all I can find > is the "stock_reply" method. If you look at the code in the libs/network/test/hello_world.cpp -- or there abouts I don't have the directory handy at the moment -- you should see something like this: response << header("Content-Type", "application/octet-stream") << header("Connection", "close"); What that actually means is you're pushing in HTTP headers into the response. It's the same if you intend to push in the body of the response as well: response << body("The quick brown fox jumps over the lazy dog."); You can find information about this in the documentation for the HTTP Response in the library: http://cpp-netlib.github.com/reference_http_response.html HTH -- Dean Michael Berris about.me/deanberris |
From: David H. <dav...@gm...> - 2011-01-28 22:22:36
|
On Thu, Jan 27, 2011 at 9:45 PM, Dean Michael Berris <mik...@gm... > wrote: > On Fri, Jan 28, 2011 at 9:35 AM, David Hite <dav...@gm...> wrote: > > My question is that I have been using stock_reply for the response and am > > wondering if there are other methods for sending messages. > > > > The line I have been using is: > > "response = Server::response::stock_reply(ok, msg);" > > In all the examples I see and also looking through the code all I can > find > > is the "stock_reply" method. > > If you look at the code in the libs/network/test/hello_world.cpp -- or > there abouts I don't have the directory handy at the moment -- you > should see something like this: > > response << header("Content-Type", "application/octet-stream") > << header("Connection", "close"); > > What that actually means is you're pushing in HTTP headers into the > response. It's the same if you intend to push in the body of the > response as well: > > response << body("The quick brown fox jumps over the lazy dog."); > > You can find information about this in the documentation for the HTTP > Response in the library: > http://cpp-netlib.github.com/reference_http_response.html > > HTH > > > -- > Dean Michael Berris > about.me/deanberris > > > ------------------------------------------------------------------------------ > Special Offer-- Download ArcSight Logger for FREE (a $49 USD value)! > Finally, a world-class log management solution at an even better > price-free! > Download using promo code Free_Logger_4_Dev2Dev. Offer expires > February 28th, so secure your free ArcSight Logger TODAY! > http://p.sf.net/sfu/arcsight-sfd2d > _______________________________________________ > Cpp-netlib-devel mailing list > Cpp...@li... > https://lists.sourceforge.net/lists/listinfo/cpp-netlib-devel > Hi Dean, Thanks for the quick response. Sorry for the trouble but I'm getting a compile error. Error: 'body' is not a member of 'boost::network::http::basic_response<boost::network::http::tags::http_server>' Here's the code: response << Server::response::body(buffer); using Server defined as typedef http::server<CgiResourceManager> Server; I'm using cpp-netlib v0.8 Any ideas? Thanks, -Dave- |
From: David H. <dav...@gm...> - 2011-01-28 23:08:37
|
On Fri, Jan 28, 2011 at 3:22 PM, David Hite <dav...@gm...> wrote: > > On Thu, Jan 27, 2011 at 9:45 PM, Dean Michael Berris < > mik...@gm...> wrote: > >> On Fri, Jan 28, 2011 at 9:35 AM, David Hite <dav...@gm...> wrote: >> > My question is that I have been using stock_reply for the response and >> am >> > wondering if there are other methods for sending messages. >> > >> > The line I have been using is: >> > "response = Server::response::stock_reply(ok, msg);" >> > In all the examples I see and also looking through the code all I can >> find >> > is the "stock_reply" method. >> >> If you look at the code in the libs/network/test/hello_world.cpp -- or >> there abouts I don't have the directory handy at the moment -- you >> should see something like this: >> >> response << header("Content-Type", "application/octet-stream") >> << header("Connection", "close"); >> >> What that actually means is you're pushing in HTTP headers into the >> response. It's the same if you intend to push in the body of the >> response as well: >> >> response << body("The quick brown fox jumps over the lazy dog."); >> >> You can find information about this in the documentation for the HTTP >> Response in the library: >> http://cpp-netlib.github.com/reference_http_response.html >> >> HTH >> >> >> -- >> Dean Michael Berris >> about.me/deanberris >> >> >> ------------------------------------------------------------------------------ >> Special Offer-- Download ArcSight Logger for FREE (a $49 USD value)! >> Finally, a world-class log management solution at an even better >> price-free! >> Download using promo code Free_Logger_4_Dev2Dev. Offer expires >> February 28th, so secure your free ArcSight Logger TODAY! >> http://p.sf.net/sfu/arcsight-sfd2d >> _______________________________________________ >> Cpp-netlib-devel mailing list >> Cpp...@li... >> https://lists.sourceforge.net/lists/listinfo/cpp-netlib-devel >> > > Hi Dean, Thanks for the quick response. > > Sorry for the trouble but I'm getting a compile error. > > Error: 'body' is not a member of > 'boost::network::http::basic_response<boost::network::http::tags::http_server>' > > Here's the code: > > response << Server::response::body(buffer); > > using Server defined as > > typedef http::server<CgiResourceManager> Server; > > I'm using cpp-netlib v0.8 > > Any ideas? > Thanks, > -Dave- > Hello, Ok, so I figured out how to directly set the response message. For example: response.content = "Hello world!" response.status = Server::response::ok; http::request_header<http::tags::http_server> content_type = {"Content-Type", "text/html"}; response.headers.push_back( content_type ); However, now I have the problem that response.content is a string. How do I send binary data, such as an image file? Thanks! -Dave- |
From: Dean M. B. <mik...@gm...> - 2011-01-29 13:33:54
|
On Sat, Jan 29, 2011 at 7:08 AM, David Hite <dav...@gm...> wrote: > >> >> response << Server::response::body(buffer); >> This should just be: namespace http = boost::network::http; response << http::body(buffer); > > However, now I have the problem that response.content is a string. How do I > send binary data, such as an image file? > The way I would do it is to somehow mmap the file, get the mmap'ed pointer, and copy the data into response.content directly. Binary data is held fine by std::string instances, you just have to reserve the appropriate amount of memory when you're doing that. HTH -- Dean Michael Berris about.me/deanberris |
From: Nelson, E. - 2 <eri...@ba...> - 2011-01-31 19:09:31
|
> Dean Michael Berris wrote on Saturday, January 29, 2011 8:33 AM > On Sat, Jan 29, 2011 at 7:08 AM, David Hite <dav...@gm...> wrote: >> However, now I have the problem that response.content is a string. >> How do I >> send binary data, such as an image file? >> > > The way I would do it is to somehow mmap the file, get the mmap'ed > pointer, and copy the data into response.content directly. Binary data > is held fine by std::string instances, you just have to reserve the > appropriate amount of memory when you're doing that. > This question will continue to come up. It's true that std::string *can* hold binary data. It's just not *natural*. The std::string interface is strongly influenced by null-terminated strings. Things that the interface implies (like string.c_str() or string(char*) constructor actually do what you'd think) are broken if you use std::string with binary data. The more natural interface (used by asio as well) is a buffer object which is just a tuple of the form {void*, size_t}, which is easily constructed from a string, vector<char>, array, or just about any container that the user has for the data. The user should *not* be forced to make a copy of their data in a std::string just to satisfy the network library. Erik ---------------------------------------------------------------------- This message w/attachments (message) is intended solely for the use of the intended recipient(s) and may contain information that is privileged, confidential or proprietary. If you are not an intended recipient, please notify the sender, and then please delete and destroy all copies and attachments, and be advised that any review or dissemination of, or the taking of any action in reliance on, the information contained in or attached to this message is prohibited. Unless specifically indicated, this message is not an offer to sell or a solicitation of any investment products or other financial product or service, an official confirmation of any transaction, or an official statement of Sender. Subject to applicable law, Sender may intercept, monitor, review and retain e-communications (EC) traveling through its networks/systems and may produce any such EC to regulators, law enforcement, in litigation and as required by law. The laws of the country of each sender/recipient may impact the handling of EC, and EC may be archived, supervised and produced in countries other than the country in which you are located. This message cannot be guaranteed to be secure or free of errors or viruses. References to "Sender" are references to any subsidiary of Bank of America Corporation. Securities and Insurance Products: * Are Not FDIC Insured * Are Not Bank Guaranteed * May Lose Value * Are Not a Bank Deposit * Are Not a Condition to Any Banking Service or Activity * Are Not Insured by Any Federal Government Agency. Attachments that are part of this EC may have additional important disclosures and disclaimers, which you should read. This message is subject to terms available at the following link: http://www.bankofamerica.com/emaildisclaimer. By messaging with Sender you consent to the foregoing. |
From: Dean M. B. <mik...@gm...> - 2011-02-01 14:00:35
|
On Tue, Feb 1, 2011 at 3:09 AM, Nelson, Erik - 2 <eri...@ba...> wrote: >> Dean Michael Berris wrote on Saturday, January 29, 2011 8:33 AM >> On Sat, Jan 29, 2011 at 7:08 AM, David Hite <dav...@gm...> wrote: >>> However, now I have the problem that response.content is a string. >>> How do I >>> send binary data, such as an image file? >>> >> >> The way I would do it is to somehow mmap the file, get the mmap'ed >> pointer, and copy the data into response.content directly. Binary data >> is held fine by std::string instances, you just have to reserve the >> appropriate amount of memory when you're doing that. >> > > This question will continue to come up. It's true that std::string *can* hold binary data. It's just not *natural*. The std::string interface is strongly influenced by null-terminated strings. Things that the interface implies (like string.c_str() or string(char*) constructor actually do what you'd think) are broken if you use std::string with binary data. > I know. ;) Except a lot of times though, the HTTP protocol largely deals with *text transfer*. Also it's not as simple as it sounds. See below. > The more natural interface (used by asio as well) is a buffer object which is just a tuple of the form {void*, size_t}, which is easily constructed from a string, vector<char>, array, or just about any container that the user has for the data. The user should *not* be forced to make a copy of their data in a std::string just to satisfy the network library. > And this interface is already available in 0.8.1, in the asynchronous server type. Basically now (thanks to Oleg Malashenko) you can do `write(boost::asio::const_buffers_1(ptr, size))` and it won't make a copy. The problem we have here in the synchronous server example is that the response object has to be created before we even pass it into the handler's operator()(...) overload. It also means that the data will have to be "accessible" until the internal write completion handler is invoked by Boost.Asio *after the handler's operator() overload is finished*. This means the data that you have to put in the response object that is passed into the synchronous server implementation, should out-live the scope of the handler's operator() overload. I see two possibilities moving forward here: 1. ) Break the interface of the server response type and require that data to be placed into the response object's body should be a variant of either a string or a boost::tuple<shared_ptr<void>, size_t> -- which I'm not even positive will work as I expect because what's in the shared_ptr<void> should be an object and not just an arbitrary pointer. This is going to make the server template's connection implementation a lot more tricky than it has to be. 2. ) Deprecate the synchronous server template in favor of the asynchronous server template. This will remove the simplicity of the approach provided for in the implementation. Yet another way is to put the burden of managing the memory of the data to be written out by the server, by providing an optional callback function when providing the content. So it would then be a variant between a string and a tuple<void*, size_t, function<void(void*)> >. All of these options makes the synchronous handler's implementation needlessly complex. Of course, any ideas and/or patches would be most welcome especially if makes things simple. :) -- Dean Michael Berris about.me/deanberris |
From: David H. <dav...@gm...> - 2011-01-31 19:49:38
|
On Mon, Jan 31, 2011 at 12:09 PM, Nelson, Erik - 2 < eri...@ba...> wrote: > > Dean Michael Berris wrote on Saturday, January 29, 2011 8:33 AM > > On Sat, Jan 29, 2011 at 7:08 AM, David Hite <dav...@gm...> wrote: > >> However, now I have the problem that response.content is a string. > >> How do I > >> send binary data, such as an image file? > >> > > > > The way I would do it is to somehow mmap the file, get the mmap'ed > > pointer, and copy the data into response.content directly. Binary data > > is held fine by std::string instances, you just have to reserve the > > appropriate amount of memory when you're doing that. > > > > This question will continue to come up. It's true that std::string *can* > hold binary data. It's just not *natural*. The std::string interface is > strongly influenced by null-terminated strings. Things that the interface > implies (like string.c_str() or string(char*) constructor actually do what > you'd think) are broken if you use std::string with binary data. > > The more natural interface (used by asio as well) is a buffer object which > is just a tuple of the form {void*, size_t}, which is easily constructed > from a string, vector<char>, array, or just about any container that the > user has for the data. The user should *not* be forced to make a copy of > their data in a std::string just to satisfy the network library. > > Erik > > > _______________________________________________ > Cpp-netlib-devel mailing list > Cpp...@li... > https://lists.sourceforge.net/lists/listinfo/cpp-netlib-devel > Hey Erik, Good point! I agree. vector<char> seems to be a good standard. In fact, it looks like vector<char> is (or will be) the mandated underlying storage container for std::string. For info, here's what I have working. It seems the easy pitfall is using assignment operator "=" with a char* buffer for a string assumes NULL terminated string. The alternative is to use the string constructor with a char* and size. Here's the code to load binary files (i.e. images) and serve with synchronous server. namespace http = boost::network::http; namespace utils = boost::network::utils; namespace fs = boost::filesystem; void serveFile(const Server::request &request, Server::response &response){ // get destination request for image file Server::string_type dest = destination(request); // internal document root string doc_root_path = "/path/to/doc_root" // set boost::filesystem::path (using filesystem "/" operator for directory/path concatenation fs::path file(doc_root_path / dest); // optional... perform error detection on existence of file... ifstream infile(file.c_str()); int size = fs::file_size(file); char *buf = new char[size]; infile.read(buf, size); // copy binary file into string string bin_str(buf, read_count); // set response contents response.content = bin_str; // set status response.status = Server::response::ok; // optional... check for file extension and assign mime type accordingly... // string file_ext = file.extension().c_str(); // Generic mime type for image (binary) file types http::request_header<http::tags::http_server> content_type = {"Content-Type", "x-application/octet-stream"}; response.headers.push_back( content_type ); } -Dave- |
From: Dean M. B. <mik...@gm...> - 2011-02-01 14:11:40
|
On Tue, Feb 1, 2011 at 3:49 AM, David Hite <dav...@gm...> wrote: > > On Mon, Jan 31, 2011 at 12:09 PM, Nelson, Erik - 2 > <eri...@ba...> wrote: > > Hey Erik, > Good point! I agree. vector<char> seems to be a good standard. Actually what would be better would a vector<uint8_t> so you don't miss the 8-bit goodness of the data but that's beside the point. ;) > In fact, it > looks like vector<char> is (or will be) the mandated underlying storage > container for std::string. > Yeah, unfortunately. But that's another discussion. :P > For info, here's what I have working. It seems the easy pitfall is using > assignment operator "=" with a char* buffer for a string assumes NULL > terminated string. The alternative is to use the string constructor with a > char* and size. > > Here's the code to load binary files (i.e. images) and serve with > synchronous server. > Thanks David! :) Would you mind contributing this as an example to the project? :D Have a good one and I hope to hear from you again soon! :) -- Dean Michael Berris about.me/deanberris |
From: Nelson, E. - 2 <eri...@ba...> - 2011-02-01 17:04:24
|
> From: Dean Michael Berris wrote on Tuesday, February 01, 2011 9:00 AM >> On Tue, Feb 1, 2011 at 3:09 AM, Nelson, Erik - 2 wrote: >>> Dean Michael Berris wrote on Saturday, January 29, 2011 8:33 AM >>> The way I would do it is to somehow mmap the file, get the mmap'ed >>> pointer, and copy the data into response.content directly. Binary >>> data is held fine by std::string instances, you just have >>> to reserve the appropriate amount of memory when you're doing that. >> This question will continue to come up. It's true that std::string >> *can* hold binary data. It's just not *natural*. The std::string >> interface is strongly influenced by null-terminated strings. Things >> that the interface implies (like string.c_str() or string(char*) >> constructor actually do what you'd think) are broken if you use >> std::string with binary data. > > I know. ;) > > Except a lot of times though, the HTTP protocol largely deals with > *text transfer*. Also it's not as simple as it sounds. See below. > The fact that it's a common use case (I'd suggest that actually much more binary image data is transmitted than text) means that the library should easily accommodate text transfer. I think this goes back to an underlying assumption that you expressed some time ago- iirc, you believed that HTTP could *only* transmit text, and that binary data needed to be base64'd into text before transmission. That assumption may still be informing your design choices here, but the very suggestion of copying binary data into a string should (in my opinion) is a red flag that there's something wrong with the interface. Having an overload that makes std::string usage natural (like it is now) is good thing. Forcing someone to copy memory regions into a std::string is a bad thing. >> The more natural interface (used by asio as well) is a buffer object >> which is just a tuple of the form {void*, size_t}, which is easily >> constructed from a string, vector<char>, array, or just about any >> container that the user has for the data. The user should *not* be >> forced to make a copy of their data in a std::string just to satisfy >> the network library. > > And this interface is already available in 0.8.1, in the asynchronous > server type. Basically now (thanks to Oleg Malashenko) you can do > `write(boost::asio::const_buffers_1(ptr, size))` and it won't make a > copy. > > The problem we have here in the synchronous server example is that the > response object has to be created before we even pass it into the > handler's operator()(...) overload. It also means that the data will > have to be "accessible" until the internal write completion handler is > invoked by Boost.Asio *after the handler's operator() overload is > finished*. This means the data that you have to put in the response > object that is passed into the synchronous server implementation, > should out-live the scope of the handler's operator() overload. > > I see two possibilities moving forward here: > > 1. ) Break the interface of the server response type and require that > data to be placed into the response object's body should be a variant > of either a string or a boost::tuple<shared_ptr<void>, size_t> -- > which I'm not even positive will work as I expect because what's in > the shared_ptr<void> should be an object and not just an arbitrary > pointer. This is going to make the server template's connection > implementation a lot more tricky than it has to be. > > 2. ) Deprecate the synchronous server template in favor of the > asynchronous server template. This will remove the simplicity of the > approach provided for in the implementation. > > Yet another way is to put the burden of managing the memory of the > data to be written out by the server, by providing an optional > callback function when providing the content. So it would then be a > variant between a string and a tuple<void*, size_t, > function<void(void*)> >. All of these options makes the synchronous > handler's implementation needlessly complex. My original point was only about the inelegance of using std::string. The point you're raising here is a different one, and all you're saying is that the lifetime of the payload needs to be roughly the same as the lifetime of the result object. That's not a terribly complicated- problem, and there are lots of solutions to it. It seems that one that's easy for the user is to just hand off ownership to the response, something like auto_ptr<MyObject> obj(new MyObject); response << body(obj); should be sufficient for POD types, and for non-POD types, maybe you'd need something like auto_ptr<vector<char> > obj(new vector<char>()); response << body(&(*obj)[0], obj->size(), obj); The response can delete it whenever it's done with doing whatever it does. That seems to me to be pretty easy and intuitive for the user. Erik ---------------------------------------------------------------------- This message w/attachments (message) is intended solely for the use of the intended recipient(s) and may contain information that is privileged, confidential or proprietary. If you are not an intended recipient, please notify the sender, and then please delete and destroy all copies and attachments, and be advised that any review or dissemination of, or the taking of any action in reliance on, the information contained in or attached to this message is prohibited. Unless specifically indicated, this message is not an offer to sell or a solicitation of any investment products or other financial product or service, an official confirmation of any transaction, or an official statement of Sender. Subject to applicable law, Sender may intercept, monitor, review and retain e-communications (EC) traveling through its networks/systems and may produce any such EC to regulators, law enforcement, in litigation and as required by law. The laws of the country of each sender/recipient may impact the handling of EC, and EC may be archived, supervised and produced in countries other than the country in which you are located. This message cannot be guaranteed to be secure or free of errors or viruses. References to "Sender" are references to any subsidiary of Bank of America Corporation. Securities and Insurance Products: * Are Not FDIC Insured * Are Not Bank Guaranteed * May Lose Value * Are Not a Bank Deposit * Are Not a Condition to Any Banking Service or Activity * Are Not Insured by Any Federal Government Agency. Attachments that are part of this EC may have additional important disclosures and disclaimers, which you should read. This message is subject to terms available at the following link: http://www.bankofamerica.com/emaildisclaimer. By messaging with Sender you consent to the foregoing. |
From: Dean M. B. <mik...@gm...> - 2011-02-01 18:20:22
|
On Wed, Feb 2, 2011 at 1:04 AM, Nelson, Erik - 2 <eri...@ba...> wrote: >> From: Dean Michael Berris wrote on Tuesday, February 01, 2011 9:00 AM >>> This question will continue to come up. It's true that std::string >>> *can* hold binary data. It's just not *natural*. The std::string >>> interface is strongly influenced by null-terminated strings. Things >>> that the interface implies (like string.c_str() or string(char*) >>> constructor actually do what you'd think) are broken if you use >>> std::string with binary data. >> >> I know. ;) >> >> Except a lot of times though, the HTTP protocol largely deals with >> *text transfer*. Also it's not as simple as it sounds. See below. >> > > The fact that it's a common use case (I'd suggest that actually much more binary image data is transmitted than text) means that the library should easily accommodate text transfer. I think this goes back to an underlying assumption that you expressed some time ago- iirc, you believed that HTTP could *only* transmit text, and that binary data needed to be base64'd into text before transmission. That assumption may still be informing your design choices here, but the very suggestion of copying binary data into a string should (in my opinion) is a red flag that there's something wrong with the interface. > Well, the problem with sending binary data as 7-bit clear over the network has been documented extensively over the Internet. The spec clearly states that you have to be transferring data safe to transfer over 7-bit transfers encoding -- meaning that's technically ASCII text. It is an accident that images are being sent in the clear as binary data, and if you notice in history this is largely why people (browser developers and server developers) had to agree that they would just take whatever was sent over the wire in the body and just have the MIME identifiers there. The design of the library has actually nothing to do with whether I think text should be the only way transmitted over HTTP. If you also notice the type of the string is parametric to the tag type used. It makes it *easy* to just use std::string. I could very well be implementing a chained-block-data-structure for the underlying message storage and manipulate those directly and expose ranges for the accessor/wrappers (like how ACE does it) but that's too much work to do at the moment -- patches to implement this would be most welcome. ;) At any rate, the reason why it's technically better to send things via HTTP using Base64 encoding is really just so that you're OK as far as the spec goes. This avoids all the endianness issues you might encounter on the other end (although Boost.Asio should be dealing with that issue for us). It's also largely a matter of convenience -- it's perfectly *fine* to put binary data in an std::string or an std::vector. > Having an overload that makes std::string usage natural (like it is now) is good thing. Forcing someone to copy memory regions into a std::string is a bad thing. > The reason the copy is forced is for simplicity of the implementation. Again if you wanted to use a no-copy or single-copy interface, use the asynchronous server implementation *today*. ;) >> >> Yet another way is to put the burden of managing the memory of the >> data to be written out by the server, by providing an optional >> callback function when providing the content. So it would then be a >> variant between a string and a tuple<void*, size_t, >> function<void(void*)> >. All of these options makes the synchronous >> handler's implementation needlessly complex. > > My original point was only about the inelegance of using std::string. The point you're raising here is a different one, and all you're saying is that the lifetime of the payload needs to be roughly the same as the lifetime of the result object. That's not a terribly complicated- problem, and there are lots of solutions to it. It seems that one that's easy for the user is to just hand off ownership to the response, something like > See, the fact that I'm even using std::string is already something I detest (read the thread about [string]proposal on the Boost ML ;) ) -- but at the moment it is the most sane and simple thing to do lacking a proper efficient segmented data storage mechanism around (no std::deque has its own issues, and ptr_list<array<T,N> > is too "esoteric"). Forcing people to deal with std::vector<char> is just unnecessary when std::string is much more familiar however ugly. Of course nothing's stopping you or anyone to create a different tag type that defines string<Tag>::type as std::vector<char> or anything for that matter. ;) The idea is really, to make the hard thing simple to do -- imagine having to implement your own HTTP server, and you might think whether you're making a copy of the data one extra time is *not* that big of a deal. Of course I'd like to improve the library, it's just that, well, if only more people submitted pull requests and actually addressed it, we might have a better library to use sooner than later. :P > > auto_ptr<MyObject> obj(new MyObject); > response << body(obj); > Uh oh, this is dangerous because the user can use the obj right after the data is passed to the response. > should be sufficient for POD types, and for non-POD types, maybe you'd need something like > > auto_ptr<vector<char> > obj(new vector<char>()); > response << body(&(*obj)[0], obj->size(), obj); > And this is just ugly. ;) > The response can delete it whenever it's done with doing whatever it does. > That's bad design. :D > That seems to me to be pretty easy and intuitive for the user. > Unfortunately, that's not much better than saying: std::ifstream f(...); response.content.reserve(file_size); f.read(response.content.data(), file_size); Am I missing something? For data that's already in memory I get the utility of being able to refer to the bytes directly. Unfortunately making the library deallocate memory that the user allocated is, quite bluntly, bad form and error prone. Makes sense? BTW I should really be asleep right now, but I just couldn't wait until I'm awake post my side of the story as to why things are as they are for the synchronous server. For the asynchronous server, this is largely a non-issue. ;) -- Dean Michael Berris about.me/deanberris |
From: Nelson, E. - 2 <eri...@ba...> - 2011-02-01 19:25:07
|
> Dean Michael Berris wrote on Tuesday, February 01, 2011 1:20 PM > > Well, the problem with sending binary data as 7-bit clear over the > network has been documented extensively over the Internet. The spec > clearly states that you have to be transferring data safe to transfer > over 7-bit transfers encoding -- meaning that's technically ASCII > text. It is an accident that images are being sent in the clear as > binary data, and if you notice in history this is largely why people > (browser developers and server developers) had to agree that they > would just take whatever was sent over the wire in the body and just > have the MIME identifiers there. > Either way you're sending binary in these examples... the only issue was whether that binary should be copied into a std::string > The design of the library has actually nothing to do with whether I > think text should be the only way transmitted over HTTP. If you also > notice the type of the string is parametric to the tag type used. It > makes it *easy* to just use std::string. I could very well be > implementing a chained-block-data-structure for the underlying message > storage and manipulate those directly and expose ranges for the > accessor/wrappers (like how ACE does it) but that's too much work to > do at the moment -- patches to implement this would be most welcome. > ;) > > At any rate, the reason why it's technically better to send things via > HTTP using Base64 encoding is really just so that you're OK as far as > the spec goes. This avoids all the endianness issues you might > encounter on the other end (although Boost.Asio should be dealing with > that issue for us). It's also largely a matter of convenience -- it's > perfectly *fine* to put binary data in an std::string or an > std::vector. > > > Having an overload that makes std::string usage natural (like it is > now) is good thing. Forcing someone to copy memory regions into a > std::string is a bad thing. > > > > The reason the copy is forced is for simplicity of the implementation. > Again if you wanted to use a no-copy or single-copy interface, use the > asynchronous server implementation *today*. ;) > > >> > >> Yet another way is to put the burden of managing the memory of the > >> data to be written out by the server, by providing an optional > >> callback function when providing the content. So it would then be a > >> variant between a string and a tuple<void*, size_t, > >> function<void(void*)> >. All of these options makes the synchronous > >> handler's implementation needlessly complex. > > > > My original point was only about the inelegance of using std::string. > The point you're raising here is a different one, and all you're > saying is that the lifetime of the payload needs to be roughly the same > as the lifetime of the result object. That's not a terribly > complicated- problem, and there are lots of solutions to it. It seems > that one that's easy for the user is to just hand off ownership to the > response, something like > > > > See, the fact that I'm even using std::string is already something I > detest (read the thread about [string]proposal on the Boost ML ;) ) -- I've been reading the (extensive) [string] proposal on boost. :) > but at the moment it is the most sane and simple thing to do lacking a > proper efficient segmented data storage mechanism around (no > std::deque has its own issues, and ptr_list<array<T,N> > is too > "esoteric"). Forcing people to deal with std::vector<char> is just > unnecessary when std::string is much more familiar however ugly. I'm not proposing vector<char>- the foundation of my point is the library *should not* make the container choice for the user. There is no choice that works well for everyone. > > Of course nothing's stopping you or anyone to create a different tag > type that defines string<Tag>::type as std::vector<char> or anything > for that matter. ;) > > The idea is really, to make the hard thing simple to do -- imagine > having to implement your own HTTP server, and you might think whether > you're making a copy of the data one extra time is *not* that big of a > deal. Of course I'd like to improve the library, it's just that, well, > if only more people submitted pull requests and actually addressed it, > we might have a better library to use sooner than later. :P > > Erik wrote: >> >> auto_ptr<MyObject> obj(new MyObject); >> response << body(obj); >> > Uh oh, this is dangerous because the user can use the obj right after > the data is passed to the response. Nope- the auto_ptr will be cleared when the ownership transfers to the response, right? > Erik wrote: >> should be sufficient for POD types, and for non-POD types, maybe >> you'd need something like >> >> auto_ptr<vector<char> > obj(new vector<char>()); >> response << body(&(*obj)[0], obj->size(), obj); >> > > And this is just ugly. ;) A little... think of it from a user standpoint- how would you send an arbitrary object in the response body? Can you write anything cleaner or more intuitive than response << body(void*, length) ? The way you're describing makes you write response.content.resize(length); memcpy((void*)response.content.data(), &object, length); That looks less ugly? More intuitive? > Erik wrote: >>> The response can delete it whenever it's done with doing whatever it >> does. >> > > That's bad design. :D > That's what it happens in the current implementation... the only difference is that it is deleting a copy (that's in the body) instead of the original. The issue here is that the lifetime of the response object is longer than the lifetime of the handler function. The solution is going to be to have the response destroy the body memory, either in std::string form (like now) or in original-object form. Either way the response is taking ownership of the memory that it is sending down the wire. > > Unfortunately, that's not much better than saying: > > std::ifstream f(...); > response.content.reserve(file_size); > f.read(response.content.data(), file_size); > > Am I missing something? I think this example illustrates my point about the trickiness of the interface. There are two reasons this won't work. 1. response.content.data() returns const char*, right? You can't write into that. 2. After this is run, response.content.size() will always be zero, right? And nothing will be sent? You probably wrote these bugs because you're up too late. :) Still, they show the brittleness of using a string like this. > > For data that's already in memory I get the utility of being able to > refer to the bytes directly. Unfortunately making the library > deallocate memory that the user allocated is, quite bluntly, bad form > and error prone. > > Makes sense? > I'd say that passing ownership of an object to a library is neither bad form nor error prone. Sutter has a good writeup of using auto_ptr effectively in 'Exceptional C++' that seems applicable. Transferring object ownership with auto_ptr is safe, efficient, and idiomatic. Anyway, you're putting the sweat equity into cpp-netlib, and are closer to the project. My only original observation was 'The same question will come up again and again because it's a surprising interface'. It clearly can work as it is, but it is surprising. That's why he didn't figure it out by himself. Erik ---------------------------------------------------------------------- This message w/attachments (message) is intended solely for the use of the intended recipient(s) and may contain information that is privileged, confidential or proprietary. If you are not an intended recipient, please notify the sender, and then please delete and destroy all copies and attachments, and be advised that any review or dissemination of, or the taking of any action in reliance on, the information contained in or attached to this message is prohibited. Unless specifically indicated, this message is not an offer to sell or a solicitation of any investment products or other financial product or service, an official confirmation of any transaction, or an official statement of Sender. Subject to applicable law, Sender may intercept, monitor, review and retain e-communications (EC) traveling through its networks/systems and may produce any such EC to regulators, law enforcement, in litigation and as required by law. The laws of the country of each sender/recipient may impact the handling of EC, and EC may be archived, supervised and produced in countries other than the country in which you are located. This message cannot be guaranteed to be secure or free of errors or viruses. References to "Sender" are references to any subsidiary of Bank of America Corporation. Securities and Insurance Products: * Are Not FDIC Insured * Are Not Bank Guaranteed * May Lose Value * Are Not a Bank Deposit * Are Not a Condition to Any Banking Service or Activity * Are Not Insured by Any Federal Government Agency. Attachments that are part of this EC may have additional important disclosures and disclaimers, which you should read. This message is subject to terms available at the following link: http://www.bankofamerica.com/emaildisclaimer. By messaging with Sender you consent to the foregoing. |
From: Kim G. <kim...@gm...> - 2011-02-01 20:10:07
|
Erik, Dean On Tue, Feb 1, 2011 at 20:24, Nelson, Erik - 2 <eri...@ba...> wrote: > >> For data that's already in memory I get the utility of being able to >> refer to the bytes directly. Unfortunately making the library >> deallocate memory that the user allocated is, quite bluntly, bad form >> and error prone. >> >> Makes sense? > > I'd say that passing ownership of an object to a library is neither bad form nor error prone. Sutter has a good writeup of > using auto_ptr effectively in 'Exceptional C++' that seems applicable. Transferring object ownership with auto_ptr is > safe, efficient, and idiomatic. I agree with this. Using auto_ptr to transfer ownership seems like a no-brainer. However, it doesn't really solve the transfer-data-without-copy problem, at least not entirely. What if I have a char buffer I got out of some C API, and I'd like to transfer that; char buffer[1024]; unsigned long bufsize = sizeof(buffer) / sizeof(*buffer); GetUserNameA(buffer, &bufsize); // Now what? I think in the cases where your data comes from somewhere out of your control and it's not packed in an auto_ptr, you'll be just as screwed as with the current std::string -- in order to get it transferable you would have to reallocate it into an auto_ptr, and that might even involve packing it into an auto_ptr< vector<T> >, which feels pretty raw. I don't have a good idea for how to solve the lifetime issues of the payload, but an auto_ptr probably won't help (much) here. Cheers, - Kim |
From: Dean M. B. <mik...@gm...> - 2011-02-01 21:19:44
|
On Wed, Feb 2, 2011 at 3:24 AM, Nelson, Erik - 2 <eri...@ba...> wrote: >> Dean Michael Berris wrote on Tuesday, February 01, 2011 1:20 PM >> >> Well, the problem with sending binary data as 7-bit clear over the >> network has been documented extensively over the Internet. The spec >> clearly states that you have to be transferring data safe to transfer >> over 7-bit transfers encoding -- meaning that's technically ASCII >> text. It is an accident that images are being sent in the clear as >> binary data, and if you notice in history this is largely why people >> (browser developers and server developers) had to agree that they >> would just take whatever was sent over the wire in the body and just >> have the MIME identifiers there. >> > > Either way you're sending binary in these examples... the only issue was whether that binary should be copied into a std::string > Why is copying into a string a problem in the first place? >> >> See, the fact that I'm even using std::string is already something I >> detest (read the thread about [string]proposal on the Boost ML ;) ) -- > > I've been reading the (extensive) [string] proposal on boost. :) > Ah, so you know the reason why I want a better string is so that I can use it in cpp-netlib, no? :D >> but at the moment it is the most sane and simple thing to do lacking a >> proper efficient segmented data storage mechanism around (no >> std::deque has its own issues, and ptr_list<array<T,N> > is too >> "esoteric"). Forcing people to deal with std::vector<char> is just >> unnecessary when std::string is much more familiar however ugly. > > I'm not proposing vector<char>- the foundation of my point is the library *should not* make the container choice for the user. There is no choice that works well for everyone. > Right, and it has never been a choice made by the library. It just so happened that the default is std::string for the string. Anybody can extend the tag dispatch mechanisms and create their own tags that define a different string<Tag>::type to yield something other than std::string. Actually if I remember correctly, there is a body<Tag>::type metafunction that I was using to determine what the type of the body should be. Basically these extension points are still undocumented at this point and I'm thinking about how to make configuring the types easier maybe with macro's instead of forcing people who want to extend the library to learn all the traits and metafunctions that depend on the tag dispatch. ;) >> Erik wrote: >>> >>> auto_ptr<MyObject> obj(new MyObject); >>> response << body(obj); >>> > >> Uh oh, this is dangerous because the user can use the obj right after >> the data is passed to the response. > > Nope- the auto_ptr will be cleared when the ownership transfers to the response, right? > Well, after the `response << body(obj)` line, obj is still accessible to the user. It's not guaranteed that the user will do this line at the end of the handler body. Also, it's not safe to take an arbitrary void* as well and write that out to the network. That's asking for trouble not only with the protocol but with C++ in general. Also, auto_ptr is going away for good reason -- it's an awfully non-value-semantics playing type. >> Erik wrote: >>> should be sufficient for POD types, and for non-POD types, maybe >>> you'd need something like >>> >>> auto_ptr<vector<char> > obj(new vector<char>()); >>> response << body(&(*obj)[0], obj->size(), obj); >>> >> >> And this is just ugly. ;) > > A little... think of it from a user standpoint- how would you send an arbitrary object in the response body? You can't, you need to linearize it to a container that is suitable for network transfer. That's the whole point of the interface defining the types -- I'm not about to go back to the days of C where I'll just treat a thunk of memory as data that I can safely use in the library. See, the point of the interface is to make things *easy*. There's absolutely no point in allowing a pointer to an arbitrary type in memory to be serialized out to a socket. There's 0 utility in that. Remember also that we're talking about the *synchronous* implementation. This means there's defined semantics for what a response type is. If you need to be sending arbitrarily large data in memory, *don't use the synchronous interface*. Look at what the fileserver example in 0.9-devel does -- which is actually portable to 0.8.1 IIRC. > Can you write anything cleaner or more intuitive than > > response << body(void*, length) ? The way you're describing makes you write > > response.content.resize(length); > memcpy((void*)response.content.data(), &object, length); > > That looks less ugly? More intuitive? > Well, there are a few reasons why your suggestion is ugly: 1) You're passing in a naked pointer. That's C'ish of you to suggest. ;) 2) Also, it's largely bad form to try to take a void* when you can get the type of the object directly. What I aim to do as a library designer responsible for the interface and the implementation is to enforce the safety of the interface and the operations which I use. 3) There's absolutely 0 reason for you not to use the string when the semantics of the handler is to craft a string response. If you want to send data, you need to send a string in. Even if it is binary data. There is swap idiom you can use if you don't want copies. It's been with the STL since the beginning: std::string my_long_serialized_string = "..."; swap(response.content, my_long_serialized_string); // look ma, no copy > >> Erik wrote: >>>> The response can delete it whenever it's done with doing whatever it >>> does. >>> >> >> That's bad design. :D >> > > That's what it happens in the current implementation... Nope. > the only difference is that it is deleting a copy (that's in the body) instead of the original. Again, nope. The response object lives as long as the connection is alive. Nothing gets deleted until all shared references to the connection go out of scope. > The issue here is that the lifetime of the response object is longer than the lifetime of the handler function. The solution is going to be to have the response destroy the body memory, either in std::string form (like now) or in original-object form. Either way the response is taking ownership of the memory that it is sending down the wire. > So how do you intend to support request pipelining on an implementation as you propose? As it is at the moment there is no guarantee as to *when* an object will be deleted. This is a non-deterministic code path. The last thing I want to do is introduce memory management code in the network implementation when that stuff should be handled automatically. There's no *easy* way to achieve what you want to achieve with the synchronous server implementation. There's a reason why I had to write an asynchronous server implementation precisely for serving huge amounts of data, supporting a streaming use case, etc. >> >> Unfortunately, that's not much better than saying: >> >> std::ifstream f(...); >> response.content.reserve(file_size); >> f.read(response.content.data(), file_size); >> >> Am I missing something? > > I think this example illustrates my point about the trickiness of the interface. There are two reasons this won't work. > > 1. response.content.data() returns const char*, right? You can't write into that. You're right. D'oh. > 2. After this is run, response.content.size() will always be zero, right? And nothing will be sent? > Yeah. *facepalm* > You probably wrote these bugs because you're up too late. :) Still, they show the brittleness of using a string like this. > Or probably because I wasn't thinking straight generally. :P The better way is here: std::ifstream input(...); response.content.reserve(file_size); std::copy(istreambuf_iterator<char>(input.rdbuf()), istreambuf_iterator<char>(), std::back_inserter(response.content)); /me <3 the STL sometimes. ;) >> >> For data that's already in memory I get the utility of being able to >> refer to the bytes directly. Unfortunately making the library >> deallocate memory that the user allocated is, quite bluntly, bad form >> and error prone. >> >> Makes sense? >> > > I'd say that passing ownership of an object to a library is neither bad form nor error prone. Sutter has a good writeup of using auto_ptr effectively in 'Exceptional C++' that seems applicable. Transferring object ownership with auto_ptr is safe, efficient, and idiomatic. > Eh? Are you seriously suggesting to me that passing a raw pointer up to a calling function and expecting that calling function to manage the memory for you is good form? Remember, the response object is created before it's even called -- the implementation *should not be responsible for managing the memory of the extension (in which case this is the handler)*. That is what I refer to as bad form along with the ugliness that is auto_ptr. > Anyway, you're putting the sweat equity into cpp-netlib, and are closer to the project. My only original observation was 'The same question will come up again and again because it's a surprising interface'. It clearly can work as it is, but it is surprising. That's why he didn't figure it out by himself. > And the answer will be the same, turn your data into a string and stuff that into the response.content (at least for 0.8/0.8.1). With 0.9-devel there should be a better way, but nothing like what's already being suggested. I'm considering adding an optional completion callback to the response type, to allow for "chaining writes" even in the synchronous interface. I'm leaning towards the one I described earlier with a variant solution: variant< string_type, tuple< boost::asio::const_buffer, function< void(boost::asio::const_buffer, boost::system::error_code) > > > content; This means you can either assign a string to it, or assign a tuple of a boost::asio::const_buffer and a completion handler that takes care of the data in the buffer, and knows what to do with the error code passed in. It's a lot more complicated I know, but that's the only way I can think of to allow the synchronous server to deal with the case where you would like to chain writes instead of sending a big thunk of data (also a good way to enforce chunked encoding on the synchronous interface). This is a lot more scalable too, now I just have to think about whether there's a good way to do this without breaking too much code that's using the 0.8-line idiom. HTH -- Dean Michael Berris about.me/deanberris |
From: Nelson, E. - 2 <eri...@ba...> - 2011-02-01 21:41:08
|
> Kim Gräsman wrote on Tuesday, February 01, 2011 3:10 PM > > I agree with this. Using auto_ptr to transfer ownership seems like a > no-brainer. However, it doesn't really solve the > transfer-data-without-copy problem, at least not entirely. > > What if I have a char buffer I got out of some C API, and I'd like to > transfer that; > > char buffer[1024]; > unsigned long bufsize = sizeof(buffer) / sizeof(*buffer); > GetUserNameA(buffer, &bufsize); > > // Now what? > > I think in the cases where your data comes from somewhere out of your > control and it's not packed in an auto_ptr, you'll be just as screwed > as with the current std::string -- in order to get it transferable you > would have to reallocate it into an auto_ptr, and that might even > involve packing it into an auto_ptr< vector<T> >, which feels pretty > raw. > Agreed, a little raw, but clear, and not the worst thing I'll probably do today :P auto_ptr<vector<char> > buffer(new vector<char>(1024)); unsigned long bufsize = buffer->size(); GetUserNameA(&(*buffer)[0], &bufsize); buffer->resize(bufsize); response << body(buffer); not possible to get a leak, and I think there's always going to be a little gear grinding when you mix in the C API. Erik ---------------------------------------------------------------------- This message w/attachments (message) is intended solely for the use of the intended recipient(s) and may contain information that is privileged, confidential or proprietary. If you are not an intended recipient, please notify the sender, and then please delete and destroy all copies and attachments, and be advised that any review or dissemination of, or the taking of any action in reliance on, the information contained in or attached to this message is prohibited. Unless specifically indicated, this message is not an offer to sell or a solicitation of any investment products or other financial product or service, an official confirmation of any transaction, or an official statement of Sender. Subject to applicable law, Sender may intercept, monitor, review and retain e-communications (EC) traveling through its networks/systems and may produce any such EC to regulators, law enforcement, in litigation and as required by law. The laws of the country of each sender/recipient may impact the handling of EC, and EC may be archived, supervised and produced in countries other than the country in which you are located. This message cannot be guaranteed to be secure or free of errors or viruses. References to "Sender" are references to any subsidiary of Bank of America Corporation. Securities and Insurance Products: * Are Not FDIC Insured * Are Not Bank Guaranteed * May Lose Value * Are Not a Bank Deposit * Are Not a Condition to Any Banking Service or Activity * Are Not Insured by Any Federal Government Agency. Attachments that are part of this EC may have additional important disclosures and disclaimers, which you should read. This message is subject to terms available at the following link: http://www.bankofamerica.com/emaildisclaimer. By messaging with Sender you consent to the foregoing. |
From: Nelson, E. - 2 <eri...@ba...> - 2011-02-01 22:21:25
|
Dean wrote: >>> Well, the problem with sending binary data as 7-bit clear over the >>> network has been documented extensively over the Internet. The spec >>> clearly states that you have to be transferring data safe to >>> transfer over 7-bit transfers encoding -- meaning that's technically ASCII >>> text. It is an accident that images are being sent in the clear as >>> binary data, and if you notice in history this is largely why people >>> (browser developers and server developers) had to agree that they >>> would just take whatever was sent over the wire in the body and just >>> have the MIME identifiers there. >>> Erik wrote: >> Either way you're sending binary in these examples... the only issue >> was whether that binary should be copied into a std::string > > Why is copying into a string a problem in the first place? > It's not... you were talking about the extensively documented problem of sending binary as 7-bit clear on the internet- that seems unrelated to whether or not the netlib container is a std::string or something else. > >>> Erik wrote: >>>> auto_ptr<MyObject> obj(new MyObject); >>>> response << body(obj); Dean wrote: > >> Uh oh, this is dangerous because the user can use the obj right > after the data is passed to the response. Erik wrote: >> Nope- the auto_ptr will be cleared when the ownership transfers to >> the response, right? Dean wrote: > Well, after the `response << body(obj)` line, obj is still accessible > to the user. It's not guaranteed that the user will do this line at > the end of the handler body. Not so. obj is not accessible if ownership has been transferred to the response. Two auto_ptrs can't refer to the same object. Erik ---------------------------------------------------------------------- This message w/attachments (message) is intended solely for the use of the intended recipient(s) and may contain information that is privileged, confidential or proprietary. If you are not an intended recipient, please notify the sender, and then please delete and destroy all copies and attachments, and be advised that any review or dissemination of, or the taking of any action in reliance on, the information contained in or attached to this message is prohibited. Unless specifically indicated, this message is not an offer to sell or a solicitation of any investment products or other financial product or service, an official confirmation of any transaction, or an official statement of Sender. Subject to applicable law, Sender may intercept, monitor, review and retain e-communications (EC) traveling through its networks/systems and may produce any such EC to regulators, law enforcement, in litigation and as required by law. The laws of the country of each sender/recipient may impact the handling of EC, and EC may be archived, supervised and produced in countries other than the country in which you are located. This message cannot be guaranteed to be secure or free of errors or viruses. References to "Sender" are references to any subsidiary of Bank of America Corporation. Securities and Insurance Products: * Are Not FDIC Insured * Are Not Bank Guaranteed * May Lose Value * Are Not a Bank Deposit * Are Not a Condition to Any Banking Service or Activity * Are Not Insured by Any Federal Government Agency. Attachments that are part of this EC may have additional important disclosures and disclaimers, which you should read. This message is subject to terms available at the following link: http://www.bankofamerica.com/emaildisclaimer. By messaging with Sender you consent to the foregoing. |
From: Dean M. B. <mik...@gm...> - 2011-02-01 22:28:04
|
On Wed, Feb 2, 2011 at 6:21 AM, Nelson, Erik - 2 <eri...@ba...> wrote: > Dean wrote: >> >> Why is copying into a string a problem in the first place? >> > > It's not... you were talking about the extensively documented problem of sending binary as 7-bit clear on the internet- that seems unrelated to whether or not the netlib container is a std::string or something else. > Right, so why are we talking about this again? :D > >> >>>> Erik wrote: >>>>> auto_ptr<MyObject> obj(new MyObject); >>>>> response << body(obj); > > Dean wrote: >> >> Uh oh, this is dangerous because the user can use the obj right >> after the data is passed to the response. > > Erik wrote: >>> Nope- the auto_ptr will be cleared when the ownership transfers to >>> the response, right? > > > Dean wrote: >> Well, after the `response << body(obj)` line, obj is still accessible >> to the user. It's not guaranteed that the user will do this line at >> the end of the handler body. > > Not so. obj is not accessible if ownership has been transferred to the response. Two auto_ptrs can't refer to the same object. > See this code: std::auto_ptr<Foo> f(new Foo); response << body(f); // which would never happen, if I can do anything about it... ;) f->bar(); // and here you get a segfault and angry users. ;) Which is exactly what I want to avoid if I can help it. :) -- Dean Michael Berris about.me/deanberris |