繁体   English   中英

C HTTP 发布请求在第一个 200 OK 之后获得第二个响应(400 Bad Request)

[英]C HTTP post request getting a second response (400 Bad Request) after first 200 OK

我已经为这个问题苦苦挣扎了一段时间,所以是时候寻求帮助了。

系统: nRF9160 微控制器运行 Zephyr RTOS,使用蜂窝调制解调器发布到在 GCP 上运行的端点

问题:

  • 我正在使用 Zephyr 的套接字函数(我相信它与 BSD 规范匹配)来发送这样的 post 请求(见下文)。 我使用 cJSON 库将有效负载 package 向上,然后从响应中解析有效负载。
  • 当我有“连接:关闭”并且一次只尝试发送一个心跳时,这可以完美地端到端工作。 我从服务器得到 200 OK 一切都符合预期。
  • 但是,如果我尝试在 header 中使用“keep-alive”保持套接字连接打开,然后多次执行发送和接收,那么我会得到一个奇怪的响应:首先是 200 OK,然后是 400 Bad Request all from相同的帖子。

心跳发送 function

int err;
LOG_INF("Open socket");
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TLS_1_2); // Error code: 9
if (fd == -1)
{
    LOG_ERR("Failed to open socket!");
    clean_up();
}

/* Setup TLS socket options */
err = tls_setup(fd); // Error code: 7
if (err)
{
    LOG_ERR("TLS cert failure");
    clean_up();
}

// 12sec timeout for send + receive socket
socket_send_timeout(fd, 12000);
socket_recv_timeout(fd, 12000);

LOG_INF("Connecting to %s", "our.api.endpoint.com"); // us
err = connect(fd, res->ai_addr, sizeof(struct sockaddr_in)); // Error code: 10
if (err)
{
    LOG_ERR("connect() failed, err: %d", errno);
    return clean_up();
}

// [1] Empty the send buffer
memset(send_buffer, 0, sizeof(send_buffer));

// [2] Set up endpoint -> 'heartbeat'
sprintf(command, "%s", "heartbeat");

// [3] Route builder
// POST /devices/'this device'/heartbeat HTTP/1.1
// Host: our.api.endpoint.com:443
// Authorization: Bearer "auth_token"
// Connection: keep-alive
// Content-Type: application/json
// Content-Length:
sprintf(send_buffer, "%s", "POST /devices/");
strcat(send_buffer, deviceName);
strcat(send_buffer, "/");
strcat(send_buffer, command);
strcat(send_buffer, " HTTP/1.1\r\n");
strcat(send_buffer, "Host: our.api.endpoint.com:443\r\n");
strcat(send_buffer, "Authorization: Bearer ");
strcat(send_buffer, token); // Token is read from flash and stored globally
strcat(send_buffer, "\r\nConnection: keep-alive\r\n");
strcat(send_buffer, "Content-Type: application/json\r\n");
strcat(send_buffer, "Content-Length: ");

// [4] Build payload
// { "ip_address":   "172.0.0.0" }

cJSON_Init();
char *out;
cJSON *ip_address = NULL;

// Todo: Create IP string, need to implement real check here
if (modem_ip_address != NULL) {
    ip_address = cJSON_CreateString(modem_ip_address);
}
else {
    ip_address = cJSON_CreateString("Undefined modem_ip_address");
}

// create JSON root object
cJSON *root = cJSON_CreateObject();

//add fields to JSON
cJSON_AddItemToObject(root, "ip_address", ip_address);

//Print full complete JSON object
out = cJSON_Print(root);

// [5] Content-length calculate
static char len[8];
sprintf(len, "%d", strlen(out));
strcat(send_buffer, len);
strcat(send_buffer, "\r\n\r\n");

//Add JSON to request
strcat(send_buffer, out);

free(out);
cJSON_Delete(root);
int success = 0;
int num_times = 3;
for (int i = 1; i <= num_times; i++) {
    //sending request
    LOG_INF("Sending Request %d of %d", i, num_times);
    off = 0;
    LOG_INF(" -----[SEND BUFFER]-----");
    printk("%s\n", send_buffer);
    LOG_INF(" -----[BUFFER END]-----");
    do
    {
        bytes = send(fd, &send_buffer[off], sizeof(send_buffer) - off, 0); // Error code: 11
        if (bytes < 0)
        {
            LOG_ERR("send() failed, err %d", errno);
            return clean_up();
        }
        off += bytes;
    } while (off < sizeof(send_buffer) - 1);

    LOG_INF("--> Sent %d bytes", off);
    // memset(send_buffer, 0, sizeof(send_buffer));
    off = 0;
    do
    {
        bytes = recv(fd, &recv_buf[off], BUF_SIZE - off, 0);
        if (bytes < 0)
        {
            LOG_ERR("recv() failed, err %d", errno); // Error code: 12
            return clean_up();
        }
        off += bytes;
        LOG_INF("--> %d B", off);
    } while (bytes != 0 /* peer closed connection */);


    LOG_INF("----> Received %d bytes", off);
    LOG_INF(" -----[HTTP RESPONSE]-----");
    success = parse_http_ret_code(recv_buf); // return 1 if 200, return 0 otherwise
    if (success) {
        sprintf(json, strchr(recv_buf, '{'));
        printk("%s\n", json);
        success &= parse_heartbeat_response(json);
    }
    LOG_INF(" -----[HTTP END]-----");
    // TODO
    // Add error code return status so we can use that in server controller
    memset(recv_buf, 0, sizeof(recv_buf));
    memset(json, 0, sizeof(json));
}
memset(send_buffer, 0, sizeof(send_buffer));
close(fd);
return success;

使用 Keep-Alive 发布:200 OK,然后是 400 Bad Request

[00:00:12.174,926] [0m<inf> networking: Sending Request 1 of 3[0m
[00:00:12.174,926] [0m<inf> networking:  -----[SEND BUFFER]-----[0m
POST /devices/device_id/heartbeat HTTP/1.1
Host: our.api.endpoint.com:443
Authorization: Bearer token-goes-here
Connection: keep-alive
Content-Type: application/json
Content-Length: 35

{"ip_address":"172.0.0.0"}
[00:00:12.175,262] [0m<inf> networking:  -----[BUFFER END]-----[0m
[00:00:12.177,459] [0m<inf> networking: --> Sent 2048 bytes[0m
[00:00:13.144,500] [0m<inf> networking: --> 651 B[0m
[00:00:13.153,015] [0m<inf> networking: --> 799 B[0m
[00:00:13.163,330] [0m<inf> networking: --> 978 B[0m
[00:00:13.163,360] [0m<inf> networking: --> 978 B[0m
[00:00:13.163,360] [0m<inf> networking: ----> Received 978 bytes[0m
[00:00:13.163,391] [0m<inf> networking:  -----[HTTP RESPONSE]-----[0m
[00:00:13.163,391] [0m<inf> networking: 200 OK[0m
{"device_string":"device_id","ip_address":"172.0.0.0","last_heartbeat_time":"Fri, 28 Jan 2022 20:53:42 GMT","status":[2,2,2,2,2,2,2,2]}
HTTP/1.0 400 Bad Request
Content-Length: 54
Content-Type: text/html; charset=UTF-8
Date: Fri, 28 Jan 2022 20:53:42 GMT

<html><title>Error 400 (Bad Request)!!1</title></html>

关闭后发布:仅 200 OK

[00:00:29.778,015] <inf> networking:  -----[SEND BUFFER]-----
POST /device/device_id/heartbeat HTTP/1.1
Host: our.api.endpoint.com:443
Authorization: Bearer token-goes-here
Connection: close
Content-Type: application/json
Content-Length: 31

{"ip_address":"172.0.0.0"}
[00:00:29.778,350] <inf> networking:  -----[BUFFER END]-----
[00:00:29.780,578] <inf> networking: --> Sent 2047 bytes
[00:00:31.219,940] <inf> networking: --> 666 B
[00:00:31.221,282] <inf> networking: --> 818 B
[00:00:31.221,313] <inf> networking: --> 818 B
[00:00:31.221,313] <inf> networking: ----> Received 818 bytes
[00:00:31.221,313] <inf> networking:  -----[HTTP RESPONSE]-----
[00:00:31.221,343] <inf> networking: 200 OK
{"device_string":"device_id","ip_address":"172.0.0.0","last_heartbeat_time":"Thu, 27 Jan 2022 01:42:32 GMT","status":[2,2,2,2,2,2,2,2]}

This is leading me to believe I am configuring something in the HTTP header or the JSON such that the GCP load-balancer or nginx is sending back a 400 because it thinks it's seeing multiple requests. 在我的一生中,我尝试过改变一切,但无法理解错误在哪里。

同样,我尝试使用 Postman 和 Curl 和它们 function 正如预期的那样复制这些,而这些链式 400 都没有。

几个有趣的点:

  • 在 400 Bad Request 中,它似乎是来自默认 google 400 页面的 HTML(注意错误 400(错误请求).!1)。
  • 即使我使用 1.1 进行 POST 请求,它也出于某种原因显示 HTTP/1.0

会喜欢这里的任何人的想法。 我觉得这与我对 C 中的字符串构建或 cJSON 使用不当有关。

如果我正确理解了您的代码,您可以在单个 TCP 连接中执行多个 HTTP 请求。 我认为您应该为每个 HTTP 请求打开/关闭 TCP 连接。

也许 Websockets 更适合您的问题。 然后您不需要为每个请求打开一个新连接。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM