I am unable to receive the response to multiple HTTP requests when I attempt to enqueue data to send to a server.
We are able to establish a connection to a server and immediately issue an HTTP request inside the connected_callback() function (called as soon as a connection to the server is established) using the tcp_write() function. However, if I attempt to generate two HTTP resquests or more using the following syntax:
err_t connected_callback(void *arg, struct tcp_pcb *tpcb, err_t err) {
xil_printf("Connected to JUPITER server\n\r");
LWIP_UNUSED_ARG(arg);
/* set callback values & functions */
tcp_sent(tpcb, sent_callback);
tcp_recv(tpcb, recv_callback);
if (err == ERR_OK) {
char* request = "GET /circuits.json HTTP/1.1\r\n"
"Host: jupiter.info.polymtl.ca\r\n\r\n";
(void) tcp_write(tpcb, request, 100, 1);
request = "GET /livrable1/simulation.dee HTTP/1.1\r\n"
"Host: jupiter.info.polymtl.ca\r\n\r\n";
(void) tcp_write(tpcb, request, 100, 1);
tcp_output(tpcb);
xil_printf("tcp_write \n");
} else {
xil_printf("Unable to connect to server");
}
return err;}
I manage to send all of the data to the server, but I never receive any data for the second HTTP request. I manage to print the payload for the first request (the JSON file) but I never manage to receive anything for the .dee file. Are there any specific instructions to enqueue HTTP requests together with lwIP or am I missing something?
If you require any more code to accurately analyze my problem, feel free to say so.
Thanks!
The problem I see is that you have double \r\n combination at the end of your request header statement.
You need \r\n\r\n only at the end of your header. Now, you have double times. Remove from first write.
Related
I am making a post request from my ESP32 S2 Kaluga kit.
I have tested the HTTP request while running a server program in my LAN.
I am using
esp_http_client_handle_t and esp_http_client_config_t from
esp_http_client.h to do this.
Now, I have a HTTPS api setup in AWS API gateway. I get following error with https now:
E (148961) esp-tls-mbedtls: No server verification option set in esp_tls_cfg_t structure. Check esp_tls API reference
E (148961) esp-tls-mbedtls: Failed to set client configurations, returned [0x8017] (ESP_ERR_MBEDTLS_SSL_SETUP_FAILED)
E (148971) esp-tls: create_ssl_handle failed
E (148981) esp-tls: Failed to open new connection
E (148981) TRANSPORT_BASE: Failed to open a new connection
E (148991) HTTP_CLIENT: Connection failed, sock < 0
How can I solve this? Thank you
Edit:
Following is the code I use
I create a http client for post request:
esp_err_t client_event_get_handler(esp_http_client_event_handle_t evt)
{
switch (evt->event_id)
{
case HTTP_EVENT_ON_DATA:
printf("HTTP GET EVENT DATA: %s", (char *)evt->data);
break;
default:
break;
}
return ESP_OK;
}
static void post_rest_function( char *payload , int len)
{
esp_http_client_config_t config_post = {
.url = SERVER_URL,
.method = HTTP_METHOD_POST,
.event_handler = client_event_get_handler,
.auth_type = HTTP_AUTH_TYPE_NONE,
.transport_type = HTTP_TRANSPORT_OVER_TCP
};
esp_http_client_handle_t client = esp_http_client_init(&config_post);
esp_http_client_set_post_field(client, payload, len);
esp_http_client_set_header(client, "Content-Type", "image/jpeg");
esp_http_client_perform(client);
esp_http_client_cleanup(client);
}
and I use it in main with an image payload:
void app_main(){
....
post_rest_function( (char *)pic->buf, pic->len);
....
}
You need certificate to make https requests. In case you dont want to implement this, just edit your sdkconfig "Allow potentially insecure options" -> true
"Skip server certificate verification by default" -> true
Careful, this is unsafe.
Additionally, you may choose to include the certificates to make sure that your transfer is safe (valid server).
You can obtain the root SSL certificate of your host like so watch through till 56 minute mark for a complete explanation.
OR you may use the included certificate bundle that espressif provides in the IDF framework, for that:
In your code include #include "esp_crt_bundle.h"
and in your client_config_t add these:
.transport_type = HTTP_TRANSPORT_OVER_SSL, //Specify transport type
.crt_bundle_attach = esp_crt_bundle_attach, //Attach the certificate bundle
after which the process remains quite the same.
The video I linked above is quite helpful, I recommend you watch the whole thing :)
I have a server in Go that handles the files upload. It is a legacy code so I can't touch it so much.
The server should interrupt the upload if it detects some errors in the request header and it should return a message to the client that something is gone wrong.
The handler function is something like the following:
func(w http.ResponseWriter, r *http.Request) {
// Check the header. Could be more than one.
if r.Header.Get("key") == "not as expected" {
w.WriteHeader(500)
w.Write("key is wrong")
return
}
//handle the file upload
}
The header check is only an example to show the problem
The server closes the connection after Write and return from the function even if the request is not completed (file received).
On the client-side (Java) when I make a request with the key with a wrong value and the file to upload as a body, I get a broken pipe exception and It can't handle the response correctly.
Actually I can't touch the client-side code.
There is way on server side to wait until the request ends before closing the connection?
The "broken pipe" error¹ seen on the Java client suggests the client insists on sending the payload (body) of its request before attempting to read the response from the server.
In HTTP/1.1 (and 1.0), the client is correct: nothing in the spec says the client has to expect the server to respond before the whole request — that is, the header and the body, if any, — gets submitted.
In your particular case, the simplest approach is to pipe the clien's body to nowhere and after that respond with an error. One idiomatic approach is using io/ioutil.Discard type:
func(w http.ResponseWriter, r *http.Request) {
// Check the checksum header
if r.Header.Get("key") == "not as expected" {
_, err := io.Copy(ioutil.Discard, r.Body)
if err != nil {
// The client went away.
// May be log something, then bail out.
panic(http.ErrAbortHandler)
}
w.WriteHeader(500)
w.Write("key is wrong")
return
}
//handle the file upload
}
net/http.ErrAbortHandler may be used to tell the HTTP server library code the request should not be carried out the normal way.
By the way, responding with 5xx to an ill-formed client request is incorrect, you should have been using 4xx instead. But it's a legacy code, so just take this as a hint for future developments.
¹ See EPIPE in the send(2) manual page.
I'm working on communication protocol called HTTP in a PIC microcontroller interface M95 GSM module. I'm trying HTTP POST method. I've successfully posted and read the response. The problem is I can't read the response consistently from Api. I've written the code that if I can't read the response after post the payload I'm again to post the same payload for 5 times to read the response. The image I've attached you can see after posted one payload, I got "Got created response", but you see I didn't read the response after posted for 5 times.
Thanks
part of a post and read code:
bool SendAtCommand_Response(char *command, char *response,int time);
....
SendAtCommand_Response("AT+QHTTPPOST=165,25,10\r","CONNECT",2000);
/*Stream forming*/
sprintf(stream_data,"Speed=0&Imei_no=%s&Battery_voltage=%f&Fuel_voltage=%f&Latitude=%f&Longitude=%f&Ignition=%s&Gps_valid=%s&Utc_Time=%s",imei_no(),bat_vol(),fuel_vol(),gps_lat(),gps_long(),ignition_status(),gps_va(),gps_utc());
printf("Length of stream:=%d\n",strlen(stream_data));
do
{
SendAtCommand_Response(stream_data,"OK",2000);
printf("%s\n",stream_data);
Delayms(500);
clear();
SendAtCommand_Response("AT+QHTTPREAD=30\r","CONNECT",3000);
if(strstr(gprs_buffer,"CREATED"))
{
//uart1str(gprs_buffer);
uart1str("Got created response\r\n");
uart1str("\r\n");
p=5;
}
p++;
}while(p<=4);
clear();
}
I have written an Apache module that handles receiving a file from a client. I now want to send a response back to the client. I want the response to contain a string representing the file path to the file sent to the module. Since I am new to writing Apache modules I am unsure as to whether there is a response struct of some sort I need to use or if everything I need is in the request_rec passed into my handler. I noticed that ap_rprintf sends data to the client. Should I just use that? And if so, how is it sent back to the client (i.e. how can my client extract the string from things sent back to it)?
Thanks!
Edit:
I just stumbled across apr_socket_send() but I don't know if that works in this case. request_rec stores the connection, so could I create a socket to the client and send the data back that way?
Have you checked out the source code for mod_example?
Basically:
r->content_type = "text/html";
ap_send_http_header(r);
ap_rputs(DOCTYPE_HTML_3_2, r);
ap_rputs("<HTML>\n", r);
...
ap_rprintf(r, "Stuff that you want to send in the body");
...
I've been working on an application which uses a GSM modem for one of two things; check its status using the built in HTTP stack by sending a GET request to the server, or sending data to the server (using UDP). I have tried several different methods to keep this as reliable as possible, and I'm finally ready to ask for help.
My application is written for the SIMCOM908 module and the PIC18 platform (I'm using a PIC18 Explorer for development).
So the problem is sometimes the modem is busy doing something, and misses a command. As a human, I would see that and just resend the command. Adding a facility for my MCU to timeout and resend isn't an issue.
What is an issue is that the modem sends unsolicited responses after different events. When the modem changes registration status (with the cell tower) it would respond with +CGREG: 1, ... or when the GPS is ready GPS Ready. These responses can happen at any time, including in the middle of a command (like creating an IP connection).
This is a problem, because I haven't thought of a way to deal with this. My application needs to send a command (to connect to the server for example, AT+CIPSTART="UDP","example.com",5000) This command will response with 'OK', and then when the command has finished 'CONNECT OK'. However, I need to be able to react to the many other possible responses, and I haven't figured out a way of doing this. What do I need to do with my code to; wait for a response from the modem, check the response, perform an action based on that response?
I am code limited (being an 8-bit microcontroller!) and would like the keep repetition to a minimum. How can I write a response function that will take a response from the GSM module (solicited or now) and then let the rest of my program know what is happening?
Ideally, I'd like to do something with those responses. Like keep an internal state (when I hear GPS Ready, I know I can power the GPS etc.
Maybe there are some things I should think about, or maybe there's an open source project that already solves this problem?
Here's what I have so far:
/* Command responses */
enum {
// Common
OK = 0,
ERROR,
TIMEOUT,
OTHER,
// CGREG
NOT_REGISTERED,
// CGATT
NOT_ATTACHED,
// Network Status
NO_NETWORK,
// GPRS status
NO_ADDRESS,
// HTTP ACTION
NETWORK_ERROR,
// IP Stack State
IP_INITIAL,
IP_STATUS,
IP_CONFIG,
UDP_CLOSING,
UDP_CLOSED,
UDP_CONNECTING
} gsmResponse;
int gsm_sendCommand(const char * cmd) {
unsigned long timeout = timer_getCurrentTime() + 5000;
uart_clearb(GSM_UART); // Clear the input buffer
uart_puts(GSM_UART, cmd); // Send the command to the module
while (strstr(bf2, "\r") == NULL) { // Keep waiting for a response from the module
if (timeout < timer_getCurrentTime()) { // Check we haven't timed out yet
printf("Command timed out: %s\r\n", cmd);
return TIMEOUT;
}
}
timer_delay(100); // Let the rest of the response be received.
return OK;
}
int gsm_simpleCommand(const char * cmd) {
if (gsm_sendCommand(cmd) == TIMEOUT)
return TIMEOUT;
// Getting an ERROR response is quick, so if there is a response, this will be there
if (strstr(bf2, "ERROR") != NULL)
return ERROR;
// Sometimes the OK (meaning the command ran) can take a while
// As long as there wasn't an error, we can wait for the OK
while (strstr(bf2, "OK") == NULL);
return OK;
}
A simple command is any AT command that is specifically looking for OK or ERROR in response. Something like AT. However, I also use it for more advanced commands like AT+CPIN? because it means I will have captured the whole response, and can further search for the +CPIN: READY. However, none of this actually response to the unsolicited responses. In fact, the gsm_sendCommand() function will return early when the unsolicited response is received.
What a good way to manage complex, occasionally unsolicited, status messages like this? Please take note that this application is written in C, and runs on an 8bit microcontroller!
Having to handle both unsolicited messages as well as responses to requests in the same data stream is difficult since you will need to demultiplex the incoming stream and dispatch the results to the appropriate handler. It's a bit like an interrupt handler in that you have to drop what you were doing and handle this other bit of information which you were not necessarily expecting.
Some modules have a secondary serial port which can also be used for messages. If this is possible you could have unsolicited messages only appear on a single serial port while the main port is for your AT commands. This may not be possible, and some GSM modules will not support the complete command set on a secondary port.
Perhaps a better approach is to just disable unsolicited messages. Most commands all the state to be requested. eg While waiting for registration, instead of waiting for an unsolicited registration message to appear, simply poll the module for the current registration state. This allows you to always be in control, and you only have to handle the responses for the command just sent. If you're waiting for multiple events you can poll in a loop for each item in turn. This will generally make the code simpler as you only have to handle a single response at a time. The downside is that your response times are limited by your polling rate.
If you're set on continuing with the unsolicited message approach, I'd suggest implementing a small queue for unsolicited messages. While waiting for responses to a command, if the response does not match the command, just push the response on a queue. Then, when you've either received a response to your AT command or timed out you can process the unsolicited message queue afterwards.