ESP8266 Webserver Project

Moderator: Sprite_tm

User avatar
By prozac
#11501 @bjpirt: So, I was noodling over the streaming for my own purposes before all of this and I think we took two different approaches. I think the idea of a streaming upload makes sense, but what about post data that is not "streaming" but happens to exceed a single packet (think large forms)? I think we can solve this slightly more generically and let the cgiHandlers deal with the data as they see fit (parse as str or binary).

This was my philosophy :

1. We always have the content length for upload data, postLen. This should never be overridden.
2. Dimensioning of the postBuff should not happen while scanning the header as additional packets may come in which have no http header. It belongs in the section of httpdRecvCb that deals with post data.
3. I would rather we use a binary aligned boundary for postBuff, like 1024. This will make dealing with the data a little easier/more efficient, especially when flashing data since the flash is in 4K pages.
4. When postBuff is full, we should call httpdSendResp. The handler is responsible for returning HTTPD_CGI_MORE or HTTPD_CGI_DONE based on the postLen and how much post data has been received/processed by the connection.
5. If the postBuff is full but there is more data in *data, redimm the postBuff and start filling it again. It might be mice to have a "chunk counter" but not necessary.
6. Not sure if we should zero-terminate or not. Binary data wouldn't be zero terminated and string data that spans mitle packets wouldn't have terminations mid-string. eg. conn->postBuff[conn->priv->postPos]=0; //zero-terminate
7. The cgi status (HTTP_POST_DONE/MORE/etc) should be returned to the receive handler (or stored in the connData->priv). Then the logic dealing with post data in httpdRecvCb can xmit the response and prep the conn for destruction when, after calling httpdSendResp, cgiStatus == HTTP_CGI_DONE.

I think if we do this, then you could stream any amount of data you want and each cgiHandler could deal with the data as they expect it. I have some cobbled together code from my frankenstein version of esphttpd that I can try to clean up, but the long and short is I tested using curl to send a 4k binary file ( --data-binary) and it received the whole thing in chunk.

Hopefully these semi-incoherent rambling make at least some sense to you. I will try to get some cleaned up example code to you this weekend and we can figure out which approach is best in the long run and move on to the really fun stuff like web-based flashing ;)
User avatar
By bjpirt
#11520 @prozac I think we're pretty much describing the same thing with just a couple of minor details.

I implemented a system where you declare your cgi handler to be streaming, but I agree with you that there shouldn't really be a difference and that all cgi handlers should operate in the same way. So we would make the current streaming way of handling data as it is in my branch the default. Have I interpreted what you're saying correctly?

Regarding your points:
Point 1: I agree - we should just stream it out to the handler rather than chopping the body as is currently the method

Point 2: Not sure I get your point here - it makes some sense to me when parsing http requests to size the body buffer when we get the header that says how big it should be, and if it's bigger than the max buffer size then it's a streaming request.

Point 3: I'm split on this - on the one hand it's nice to be able to just take the whole packet if we size the buffer appropriately, on the other it makes me uneasy relying on the tcp packets having a fixed maximum size because if there's one thing I've learned it's that things in the real world don't operate according to the specs :-) So I think going back to 1024 bytes might be a good plan and just chunking the data up. However, this still won't necessarily give you nicely aligned data because, for example, when uploading a new file using multipart form encoding there's a chunk of secondary headers and delimiters that will throw this off, so I was expecting to have our own secondary buffer before writing to flash to deal with this.

Point 4: Agree, though we should rename it because it's no longer necessarily sending the response. Maybe something like dispatchRequest

Point 5: No need to redimension, just send it to dispatchRequest (or whatever it's called) and then start again from zero. For the chunk counter, I had a variable which, rather than chunks, counts the number of bytes that have been sent so far (postReceived) which can be used for the same thing.

Point 6: We could always autodetect this as we're parsing through the body and then set the flag appropriately?

Point 7: Agree - this makes more sense

The version in my branch can already stream any amount of data - I've tested it with a few hundred kB binary file and it seems OK. But I think that the combined approach makes sense to me as there are less special cases and we can treat all post request the same. Any code gratefully received - I was wondering about setting up a dummy pull request over on GitHub so we can take the conversation out of this epic thread - in fact, I'll do that now - check out https://github.com/bjpirt/esphttpd/pull/3

@Sprite_tm as the esp-httpd BDFL, what are your thoughts on moving all POST handling to being streaming? :-)
User avatar
By prozac
#11552 One last post before Sprite kicks us out of this thread ;)

I took the posting implementation and added a test cgi to flash to the espfs filesystem I have. Keep in mind that the fs sits at 0x41000 for bootmode 1 and 0x01000 for bootmode 2. Not sure if you want to look at including any of this upstream, but I think it would be very cool if you did.

https://github.com/billprozac/esphttpd