Skip to content

Is there a way to decrease the response time? #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
maxdd opened this issue Oct 20, 2018 · 12 comments
Closed

Is there a way to decrease the response time? #21

maxdd opened this issue Oct 20, 2018 · 12 comments
Labels

Comments

@maxdd
Copy link

maxdd commented Oct 20, 2018

Hello,
i resumed back my esp32 https domotic server a while ago.
I've also update my previous source with your new updated version in order to keep up with your fixes.
Anyway in some situations i would like to handle requests faster (right now it takes 2.4 seconds to do a digitalWrite).
I was wondering if you have any suggestion to decrease the time it takes to answer knowing my project is pretty much based on your Async example where based on GET and Parameters i do some actions (namely I/O). Ideally i would like to stay inside 1 second.
Are we hitting a uC bottleneck in your opinion?

@squonk11
Copy link
Contributor

Hello,
I am having the same problem: the response times are too long for my application. I tried to analyze the situation and found out that:

  • The start of an HTTPS connction roughly takes 1 second. Here the TLS handshake takes most of the time. Maybe a major part of this time is related to the speed of the ESP32 - but I am not sure about that.

  • If your webpage opens several connections when opening it, then there are several times this response time

Due to this I am currently working on implementing a websocket connection based on @fhessel 's sources. With this you can establish a persistent connection which just needs to be opened once. I hope that this will speed up my ESP32-communication. But this job currently is ongoing - so, nothing I could provide to you today.

@fhessel
Copy link
Owner

fhessel commented Oct 23, 2018

Are we hitting a uC bottleneck in your opinion?

The bottleneck is indeed the TLS handshake, and as far as I could investigate it, there is no way to get around it, and it will take 1-2 seconds. Even if you use the ESP32 just as a TLS client like in Espressif's WiFiClientSecure.ino example, you will find a delay of roughly 1 second if you print millis() just before and after the call to connect().

I added TLS session resumption some time ago (see f62add5). That would skip the "expensive" part of the handshake that uses public key crypto, but the client has to support it as well. You have a chance to configure/force this if the client is a standalone application (like smartphone apps), but if you want to access your ESP32 directly from within a web app, you most likely will not have a chance in doing so.

Other measures that are implemented in the library, like using Connection: keep-alive, only work for already-opened connections. And as @squonk11 already said, most browsers tend to parallel the connections, as this will speed up site rendering time for websites hosted on powerful hardware. Web sockets can be a solution to this, or you can try to do a rate limiting in your client and only send one request at a time. For example with Angular I was able to achieve it by providing a custom ConnectionBackend to the Http provider that serializes all requests at a central place in the application. With that, the browser will usually only open only one connection and reuse it.

After all, the server is still running on a microcontroller with its limited resources.

@maxdd
Copy link
Author

maxdd commented Oct 23, 2018

Regarding the session reuse, requests must come in within 5 min between each other, right?
I'm no http/https expert whatsoever but if keep-alive header can help how much time does the keep-alive param keeps alive the connection? I'm expecting to issue GETS and POSTS rather than serving contents so maybe for the HTTP sender i'm using the header param can be enough. Fortunately enough i would be satisfied to have the first get/post "slower" but the followings a little faster.

@fhessel
Copy link
Owner

fhessel commented Oct 23, 2018

Regarding the session reuse, requests must come in within 5 min between each other, right?

That's how it's currently implemented, but you may adjust the 300 to your needs. You'll to have to do it directly in the library though, as I do not expose every configuration parameter by now.

how much time does the keep-alive param keeps alive the connection?

That's defined in src/HTTPSServerConstants.hpp. Again you'll have to change the library's code directly. At the moment, it's set to 20 seconds. Keep in mind that the memory of the common ESP32 modules does not allow for more than 4-5 concurrent TLS connections, so the library limits it to 4 by default. This timeout is quite low by default to target common use cases, but if you have only a few, well-known clients, setting it to a higher value might be a better choice.
Also, the server does not support chunked data transfer (see #15) by now, so if your response is longer than HTTPS_KEEPALIVE_CACHESIZE bytes (also defined in HTTPSServerConstants.hpp), the server will also close the connection.

For both parameters, the client has to accept the server's decision and might not reuse the session or close the connection on its own.

@maxdd
Copy link
Author

maxdd commented Oct 28, 2018

Now with keep-alive it is almost perfect. What i found is that 60 sec * 5 min = 300 is not real.
It seems like sometimes it doesnt remember the connection for whatever reason.
What i found strange though is that if i use

const char* cmd_word = params->getUrlParameter(0).c_str();

I retrieve the "0" parameters correctly when is use param/hello/1

but if i use

const char* cmd_word = params->getUrlParameter(0).c_str();
const char* cmd_word1 = params->getUrlParameter(1).c_str()

then the first one is not retrieved correctly and the second one is the first value.

@fhessel
Copy link
Owner

fhessel commented Oct 28, 2018

Now with keep-alive it is almost perfect. What i found is that 60 sec * 5 min = 300 is not real.
It seems like sometimes it doesnt remember the connection for whatever reason.

Great that keep-alive works for you! For the session timeout, that's what I said about the client. You cannot be sure that it will accept the timeout, and it might open multiple TLS sessions in parallel, leading more than one handshake and more than one session ID. On reconnect, the client has to provide the ID for resumption, so that's nothing you can fully control on the server side. Even many HTTP client libraries won't let you control these details, I guess.

For the parameters, I'm not able to reproduce the behavior that you have experienced (or maybe I just don't get the problem by now). Could you please provide me with the definition of your ResourceNodes, the complete URLs you used for your requests and the expected and actual parameter values?

@maxdd
Copy link
Author

maxdd commented Oct 28, 2018

ResourceNode* urlParamNode = new ResourceNode("/param/*/*", "GET", &urlParamCallback);

This is the one i use.
I noticed that with params->getUrlParameterInt(1) everything works fine

@fhessel
Copy link
Owner

fhessel commented Oct 28, 2018

(I edited your comment as Markdown messed up the important part with the slashes and asterisks...)

I'll have a look at it.

@fhessel
Copy link
Owner

fhessel commented Oct 29, 2018

I finally could reproduce it. I assume you store permanent references to the temporary result of c_str(). Let's compare both cases, based on the Parameters example:

Example 1: Reference to result of c_str():

void handleURLParam(HTTPRequest * req, HTTPResponse * res) {
  ResourceParameters * params = req->getParams();
  const char* par1 = params->getUrlParameter(0).c_str();
  const char* par2 = params->getUrlParameter(1).c_str();
  res->print("Parameter 1: ");
  res->print(par1);
  res->print("\nParameter 2: ");
  res->print(par2);
}

Output 1: GET /param/foofoofoo/barbarbar

Parameter 1: barbarbar
Parameter 2: barbarbar

Example 2: Store std::string and call c_str() on demand:

void handleURLParam(HTTPRequest * req, HTTPResponse * res) {
  ResourceParameters * params = req->getParams();
  std::string par1 = params->getUrlParameter(0); // Store std::string here
  std::string par2 = params->getUrlParameter(1);
  res->print("Parameter 1: ");
  res->print(par1.c_str()); // Convert to c_str() on the fly
  res->print("\nParameter 2: ");
  res->print(par2.c_str());
}

Output 2: GET /param/foofoofoo/barbarbar

Parameter 1: foofoofoo
Parameter 2: barbarbar

If you write your code like in the second example, you should be fine. If you prefer to store C-like char arrays, you might want to use strncpy to move the data returned by c_str() into a buffer of which you are in control of its lifetime.

@maxdd
Copy link
Author

maxdd commented Oct 29, 2018

Ok, at least there is a trace about it. I think i might have used your example code back in july which was something like this.
Now i'm passing an Int so i don't need it anymore.

@fhessel
Copy link
Owner

fhessel commented Oct 31, 2018

Are there still things that need to be discussed, or may I close this issue for now?

If you encounter any new problems with the lib, you can of course open another issue for them.

@fhessel
Copy link
Owner

fhessel commented Nov 5, 2018

Closed for now.

@fhessel fhessel closed this as completed Nov 5, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants