Page 1 of 1

http server async response and keeping socket open

Posted: Tue Sep 24, 2019 5:55 pm
by ppisljar
Hello,

i would like to implement EventSource in http server, which requires me to keep the connections open and send data to them at later time.

I tried several aproaches so far:
- getting `fd` and `hd` from `req`, storing them and using them later from another function later with `http_default_send`... this doesnt seem to work
- using `httpd_queue_work` function inside request handler, with the idea that it can be an infinite task checking some kind of queue and outputing that uising `http_default_send` ... this also doesn't work
- ussing `httpd_queue_work` from the other function that should send the data, doesn't seem to work either

at this point i am kind of lost and am not sure how to approach this, any help would be very appreciated

here is my current code:

Code: Select all

#include "logging.h"
#include "esp_http_server.h"

static int pre_start_mem, post_stop_mem;
extern int httpd_default_send(httpd_handle_t hd, int sockfd, const char *buf, size_t buf_len, int flags);

//httpd_req_t* log_clients[4] = {nullptr, nullptr, nullptr, nullptr};
//int log_clients[4] = { -1, -1, -1, -1 };
struct async_resp_arg {
  httpd_handle_t hd;
  int fd;
};
struct async_resp_arg *log_clients[4] = {nullptr, nullptr, nullptr, nullptr};
static char buffer[512];
static int written = 0;


static void xlog_web_async(void *arg) {
  struct async_resp_arg *resp_arg = (struct async_resp_arg *)arg;
  httpd_handle_t hd = resp_arg->hd;
  int fd = resp_arg->fd;
  httpd_default_send(hd, fd, "event: start\ndata: test\n\n", sizeof("event: start\ndata: test\n\n"), 0);
  // for (;;) {
  //   printf("waiting");
  //   vTaskDelay( 1000 / portTICK_PERIOD_MS);
  // }
  // free(arg);
}


int vprintf_logging(const char *format, va_list arg)
{
    vprintf(format, arg);
    strcpy(buffer, "event: esp32\ndata: ");
    written = 19;
    written += vsnprintf(buffer+written, 511-written, format, arg);
    strcpy(buffer+written, "\n\n");
    written += 3;
    for (int i = 0; i < 4; i++) {
        if (log_clients[i] != nullptr) {
            printf("sending event to client on slot %d\n", i);
            httpd_queue_work(log_clients[i]->hd, xlog_web_async, log_clients[i]);
            // check if its still connected
            // if (nolongerconnected) { log_clients[i] = nullptr; continue; }
            //httpd_default_send(log_clients[i]->hd, log_clients[i]->fd, buffer, written, 0);
            //fwrite(log_clients[i], buffer, written);
            //fwrite(log_clients[i], "\n\n");
        }
    }

  return 0;
}

void xlog_web(httpd_req_t *req) { // this is request handler for http server
  int i = 0;
  while (log_clients[i] != nullptr && i < 4) {
    i++;
  }
  if (log_clients[i] != nullptr) {
    printf("all logging client slots taken\n");
    httpd_resp_set_status(req, "404");
    httpd_resp_sendstr_chunk(req, nullptr);
    return;
  }
  printf("using logging client slot %d\n", i);

  //log_clients[i] = (httpd_req_t*)malloc(sizeof(httpd_req_t));
  //log_clients[i] = httpd_req_to_sockfd(req);
  //memcpy((void*)(log_clients[i]), req, sizeof(httpd_req_t));
  httpd_resp_set_hdr(req, "Content-Type", "text/event-stream;charset=UTF-8");
  httpd_resp_set_hdr(req, "Cache-Control", "no-cache");
  httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  httpd_resp_set_status(req, "200");
  httpd_resp_sendstr_chunk(req, "\n");
  httpd_resp_sendstr_chunk(req, "event: start\ndata: test\n\n");

  log_clients[i] = (async_resp_arg*)malloc(sizeof(struct async_resp_arg));
  log_clients[i]->hd = req->handle;
  log_clients[i]->fd = httpd_req_to_sockfd(req);
  if (log_clients[i]->fd < 0) {
    printf("wrong fd\n");
    free(log_clients[i]);
    return;
  }
}

void init_logging() {
    esp_log_set_vprintf(vprintf_logging);
}

Re: http server async response and keeping socket open

Posted: Tue Sep 24, 2019 7:23 pm
by ppisljar
i narrowed it down to this code inside response handler not working:

Code: Select all

  const char* str = "event: test\ndata: test\n\n";
  int resp = httpd_default_send(req->handle, httpd_req_to_sockfd(req), str, strlen(str), 0);
  printf("sent with: %i", resp);
httpd_default_send does not return error (resp is correct length of str) but in the browser i don't receive any data and chrome marks request as 'failed'

that's weird, as this seems to be the exact way httpd_default_send is called from within esp_http_server

Re: http server async response and keeping socket open

Posted: Tue Sep 24, 2019 8:08 pm
by ppisljar
i figured you can't really mix httpd_resp_* with httpd_default_send ... if you want to use the later you should only use the later and also send correct HTTP headers first.