Pointer to request variable is NULL inside task even though it was assiged an address before task is executed.

noweare
Posts: 77
Joined: Tue Jul 02, 2019 11:35 am

Pointer to request variable is NULL inside task even though it was assiged an address before task is executed.

Postby noweare » Wed Nov 20, 2024 6:34 pm

Hello all,

I'm coding a web server that uses idf http_webserver api
In my uri handler I am saving the request pointer (httpd_req_t*) to a global variable of
the same type (httpd_req_t).

The uri handler calls a task via xTaskNotifygive() which takes as a parameter the global variable of type httpd_req_t
which has been assigned inside the uri handler.

In the task i save the passed parmeter (request ptr) to a local variable.

In the task loop I check the local request variable and it is NULL so that
my httpd_ws_send_frame(skt_req, &ws_audio_pkt) function fails.

My understanding is that as long as the global request variable is populated with a valid address
before the task is called then it should just work.

Can anyone shed some light on this ?

Thanks

Code snippets

Code: Select all

xTaskCreate(&read_file_task, "read_file_task", 4096, glb_req, 3, &readFileTaskHandle);  //task creation

static esp_err_t socket_handler(httpd_req_t *req)  //uri handler
glb_req = req;   //assignment to global request variable inside handler
xTaskNotifyGive(readFileTaskHandle); //exutes the task

static void read_file_task(void *arg)   //task
{
  httpd_req_t *skt_req = (httpd_req_t *)arg;  //assignment outside of task loop
  .
  .
  .
   ret = httpd_ws_send_frame(skt_req, &ws_audio_pkt); //inside task loop (fails since skt_req is NULL
  


MicroController
Posts: 1725
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Pointer to request variable is NULL inside task even though it was assiged an address before task is executed.

Postby MicroController » Wed Nov 20, 2024 8:45 pm

noweare wrote:
Wed Nov 20, 2024 6:34 pm
In my uri handler I am saving the request pointer (httpd_req_t*) to a global variable of
the same type (httpd_req_t).
Don't do that. The request data pointed to is only valid until the request handler returns. Check httpd_req_async_handler_begin().
The uri handler calls a task via xTaskNotifygive()
No, it doesn't ;-)
xTaskNotify... sends a "notification" to a task which has previously been started via xTaskCreate with some value for its void *arg (pvParameters argument to xTaskCreate). While running, the task can xTaskNotifyWait for a notification if&when it wants to wait/check for a notification, but if it doesn't it will never have any clue about the notification sent by the request handler.

noweare
Posts: 77
Joined: Tue Jul 02, 2019 11:35 am

Re: Pointer to request variable is NULL inside task even though it was assiged an address before task is executed.

Postby noweare » Thu Nov 21, 2024 3:52 pm

I am using a websockets connection as they are supported by the esp_http_server.

After an initial http connection the connection is upgraded to a websocket.
I thought I could just start streaming some audio data to the browser without dealing with requests and responses.

I am using this function:
httpd_ws_send_frame(httpd_req_t *req, httpd_ws_frame_t *pkt); which depends on req

The fact that the function uses a req as a parameter tells me my use of websockets is not what I thought.

I thought websockets do not operate using the request/response model like http(s).

Another rabbit hole ?

MicroController
Posts: 1725
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Pointer to request variable is NULL inside task even though it was assiged an address before task is executed.

Postby MicroController » Thu Nov 21, 2024 6:16 pm

noweare wrote:
Thu Nov 21, 2024 3:52 pm
Another rabbit hole ?
Nah, almost there ;-)
Check the example.
You can either use httpd_ws_send_frame() from inside the request-handling function, or, also inside the request handler, extract a file descriptor from the request which you can then store and use with httpd_ws_send_frame_async() later/from another task.

Edit:
Just looked at the documentation of the 'stable' IDF v5.3.1, and the docs seem to have dropped all websocket functions.
They're still used in the corresponding example though...
Given this inconsistency, you may want to look into using the 'new' httpd_req_async_handler_begin() which creates a copy of an httpd_req_t that you can subsequently use outside the request handler.

noweare
Posts: 77
Joined: Tue Jul 02, 2019 11:35 am

Re: Pointer to request variable is NULL inside task even though it was assiged an address before task is executed.

Postby noweare » Thu Nov 21, 2024 8:43 pm

Thanks for responding.

I extracted the file descriptor in the handler and tried to use it
in a task but no success. It changed from 51 to 0. I definitly
thought that would work.

I am sure i can do the whole thing in the handler but it would
be a lot and stop the website from responding to anything else
so the asynch worker threads seem to be the correct way
to do it, i think.

There is not much at all in the docs for the websocket but thats
ok. I can glean info from the source.
I will try using asynch_handler_begin().

I followed the asynch example and understand it. Just will take a little
time to incorporate that into my code and actually get it to work.
Stay tuned ....

MicroController
Posts: 1725
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Pointer to request variable is NULL inside task even though it was assiged an address before task is executed.

Postby MicroController » Thu Nov 21, 2024 10:39 pm

Ok, that's the other issue: You would first create (and thereby start) the task to handle that websocket transfer. Then, when the request handler 'fires', you need to do two things: 1. get the file descriptor from the request and store it 'somewhere', and 2. tell the task that it can now fetch that file descriptor and start working on it.
You can use a FreeRTOS queue for this. You can take the struct async_resp_arg from the example, which you'd populate inside the request handler, then send this struct into a queue. The task keeps waiting on that queue and whenever it receives a struct async_resp_arg from it, it uses it to handle one websocket transfer, then loops back to waiting for more from the queue; would look something like this:

Code: Select all

static const int REQUEST_QUEUE_CAPACITY = 10;

QueueHandle_t queueHandle;

// Structure to pass from HTTP request handler to the handling task
struct async_resp_arg {
    httpd_handle_t hd;
    int fd;
};

// Must run this once before starting any wsHandlingTask or the WS-HTTP server to create the queue both use to communicate.
void init() {
  // Create a queue to hold up to REQUEST_QUEUE_CAPACITY items of struct async_resp_arg:
  queueHandle = xQueueCreate( REQUEST_QUEUE_CAPACITY, sizeof(struct async_resp_arg) );
  assert( queueHandle != NULL );
}

static esp_err_t socket_handler(httpd_req_t *req) {
  struct async_resp_arg asyncReq;
  asyncReq.hd = req->handle;
  asyncReq.fd = httpd_req_to_sockfd(req);
  if ( xQueueSendToBack( queueHandle, &asyncReq, 1000 / portTICK_PERIOD_MS ) == pdTRUE ) {
    // OK: Request enqueued for processing.
    return ESP_OK;
  } else {
    // Request cannot be enqueued/handled at this time.
    return ESP_FAIL;
  }
}


void wsHandlingTask(void* arg) {
  while(1) {
    struct async_resp_arg async_req;
    if( xQueueReceive( queueHandle, &async_req, portMAX_DELAY) == pdTRUE ) {
      // Got a new request to handle; details now in async_req.
      // Handle it! 
    }
  }
}
Stay tuned ....
Will do :D

Who is online

Users browsing this forum: No registered users and 331 guests