
Hi Phil, On 19/08/07, Phil Endecott <spam_from_boost_dev@chezphil.org > wrote:
That's because form/environment data are 'meta-variables', according to the CGI specification.
Hmm, you mean rfc3875 section 4.1 "Request Meta-Variables"?
Yep. Most of the stuff that that section is describing really is meta-data, i.e.
data-about-the-data; e.g. the IP address of the HTTP client and the id of the user. Section 4.2 describes the contents of the HTML form where it's called simply "request data".
At least, that's the case for POST data. For GET data, it's true that the form data is in QUERY_STRING which the spec groups with the meta-data, and according to section 4.3.1 "The GET method indicates that the script should produce a document based on the meta-variable values." But you're hiding the difference between GET and POST form data, and I suggest that you apply common sense rather than just copying the spec: call the meta-data meta-data, and call the data data.
What you say makes perfect sense, unfortunately... To be honest, the reason for referring to the data as 'meta-data' is because I thought having getter functions called 'meta_*' was the least ambiguous way to do it (and obviously I thought the term was accurate). The 'get' and 'post' keywords seem to make most alternatives sound like they're doing something they're not. The previous iteration used: var<GET>() var<POST>() ... The change of heart came because that gets very ugly when you need to use qualified names. Still, if calling them all meta_* is plain wrong, then it has to change. - I think it's important, especially for a Boost library, that you
supply standard-library-like interfaces where possible. For example, I would like to see the form variables having a std::map-like interface. (This would be most easily achieved by actually making them a std::map, but if you choose some other implementation structure then you can use Boost.Iterator to help with the interface). This would allow me to iterate through them. At present, I don't think you have a way to get the names of all the form variables.
Oops, forgot to document this! There is a way to do this actually: cgi::request req; cgi::map& form_map = req.meta_form(); cgi::map is a typedef for map<string,string> for now, but this should probably be a multimap. I've made a note about where this might be heading in the 'future development' section of the docs (linked from the first page). Note that this can't currently be used for environment variables: this will probably change, but with the warning that it's going to be much slower for standard CGI.
- I don't see a way to use the form data parsers independently. For example, if I write my own code for an Apache module or a stand-alone HTTP server, I would like to be able to invoke your parser to decipher the urlencoded or multipart/form-data from a string that I supply. You could provide this as an independent header file.
Currently it's an implementation detail. It would be nice to make this visible to library users, but I'm not sure what we're using now is generic enough to make that sensible. We'll see, I suppose. :)
- In the '10 minute intro', you have: std::string user_name( req.meta_cookie("user_name") ); if (!user_name.empty()) { .... So is it impossible to distinguish between an empty string and a form or cookie value not being set?
Not really... I was thinking about this earlier and it could be done easily enough (I won't go into it, if you don't mind), but it would be a bit ugly, say something like: std::string name( req.meta_get("name") ); if (req.is_unset(name)) // var was not set There are a couple of other possibilities, one of which is to use a `cgi::param` instead of `std::string`s. Then you could do: cgi::param& name = req.meta_form("name"); if (name.empty()) // `name` contains no data if (name.unset()) // "name" is not a form variable Another option is to set the error_code value to something like error::unset in the case where the error-checked version of meta_* is used: boost::system::error_code ec; std::string name( req.meta_post("name", ec) ); if (name.empty()) // `name` contains no data if (ec ==error::unset) // "name" is not a POST variable Thoughts?
- I'm not sure about the idea of sending the response to the request object. I would think of the request object as a const thing. When I see req << "something..." I thing that that's something that should only happen when the request object is being created e.g. to set the POST data. In my apps I have always had a handler function that returns a response: HTTP::Response handle ( const HTTP::Request& req ) { ... } but this doesn't fit as well into your arrangement.
I'm not sure I follow. The request object doesn't support operator<<. You write to a request by using something like: cgi::request req; std::string response("Content-type: text/plain\r\n\r\nHello"); // variation 1 cgi::write(req, cgi::buffer(response)); // variation 2 cgi::async_write(req, cgi::buffer(response)); // variation 3 cgi::reply rep; rep<< response; rep.send(req); Perhaps the docs are misleading?
Perhaps you need a "Message" object to contain a request and the corresponding response? ("Message" is the RFC2616 term for a request/response pair.)
As it develops, it seems more sensible to drop the 'reply' object entirely and provide a single iostream interface on top of the request, much like there is an iostream above a tcp::socket in Boost.Asio: That design has been approved by boost members already and seems to work quite nicely.
- The term "Response" is normally used rather than "Reply" (e.g. in the HTTP spec).
Noted, thanks.
- For multipart/form-data, each form variable has some associated metadata; e.g. for a file upload, the filename and possibly the mime type are supplied by the browser, and the charset may be indicated for other variables. I don't see any way to get at this.
That's because there isn't. ;) You can expect this to be available eventually. Thanks for the pointers, Darren