我正在使用嵌入式linux,C和openSSL。我正在使用GET命中我的REST服务器并期待一个json对象响应。服务器获取正确的REST Get并正确处理它。但是,由于某种原因,bytes = ssl_read()没有正确接收json对象。我知道服务器正在发送正确的json对象,因为我可以使用Postman或Curl脚本命中服务器,并获得正确的json对象。此外,记录器(调试)显示服务器正确响应正确的json对象。如果我修改服务器端点代码以响应字符串并返回jsonObject.ToString()我得到正确的响应,但它是一个字符串。为什么我没有得到一个好的json对象?这是一个缩写代码清单,
// Define some parameters
int sockfd, bytes_read;
struct sockaddr_in dest;
struct hostent *hostent;
unsigned short svrport = 8443;
char hdr[4000];
char buffer[4000];
// Initialise Crypto library
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
OPENSSL_config(NULL);
// Create GET status HTTP Client Message String
bzero(hdr, sizeof(hdr));
strcpy(hdr, "GET /status HTTP/1.1\r\n");
strcat(hdr, "Host: xxx.xxx.xxx.xxx\r\n");
strcat(hdr, "X-BMID-ID: 1234-5678\r\n");
strcat(hdr, "Authorization: ");
strcat(hdr, sttb64e);
strcat(hdr, "\r\n");
strcat(hdr, "Accept: application/json\r\n");
strcat(hdr, "Content-Type: application/json\r\n");
strcat(hdr, "Connection: close\r\n");
strcat(hdr, "\r\n");
// Initialize server address/port struct
bzero(&dest, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(svrport);
dest.sin_addr.s_addr = inet_addr("xxx.xxx.xxx.xxx");
if ( dest.sin_addr.s_addr == INADDR_NONE ) {
printf("Incorrect Address Expression\n");
return 0;
}
// Connect Socket
if ( connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0 ) {
printf("Socket Connection Failed\n");
close(sockfd);
return 0;
}
// Now initialize SSL Engine
SSL_METHOD const *method;
SSL_CTX *ctx;
SSL *ssl;
SSL_library_init();
OpenSSL_add_ssl_algorithms();
SSL_load_error_strings();
method = TLSv1_2_client_method();
ctx = SSL_CTX_new(method);
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
// Make sure SSL Engine is ok
if (ctx == NULL) {
printf("SSL Engine initialization failed\n");
ERR_print_errors_fp(stderr);
close(sockfd);
return 0;
}
// Now create the SSL connection state
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sockfd);
// Check to see if the SSL socket was connected
if (SSL_connect(ssl) == FAIL) {
printf("SSL socket failed to connect\n");
return 0;
}
// Now Send Secure Message
bzero(buffer, sizeof(buffer));
SSL_write(ssl, hdr, strlen(hdr));
bytes = SSL_read(ssl, buffer, sizeof(buffer));
buffer[bytes] = 0;
if (verbose) {
printf("Received Header:\n");
printf("%s\n", buffer);
}
bzero(buffer, sizeof(buffer));
bytes = SSL_read(ssl, buffer, sizeof(buffer)); <=== **** Error is Here ****
buffer[bytes] = 0;
printf("Received Body:%s\n\n", buffer);
printf("Buffer Length = %i\n\n", bytes);
SSL_free(ssl);
// Shut down connection
close(sockfd);
SSL_CTX_free(ctx);
&#34;收到的正文:只有2个字符,总是数字,缓冲区长度是4个字节。真正的json对象看起来像这样,
{"status":"OK","shadowRequest":"0","serviceCount":1}
就像我说的,这段代码工作正常,除了它没有得到正确的json对象存储在缓冲区中。有问题的ssl_read()位于列表的末尾并标记为&lt; === ****错误在这里****。有什么想法吗?
答案 0 :(得分:2)
您需要了解转移编码&#34; chunked&#34;这不仅仅是纯文本。您要求sizeof(buffer)
,但正确的方法是阅读,直到您发现来自服务器的0
作为单行文本(,据我记忆)意味着块已完成。
我为我正在处理的项目创建了一个简单的HTTP库。我首先分析了首先对3件事感兴趣的HTTP头
Content-Length 标头:如果存在,只需获取值并在单个读取中从服务器读取该值,但这意味着读取循环OpenSSL并不那么简单。
传输编码标头:如果内容长度不存在,您可以在此确定如何读取数据。
内容编码标头:用于确定内容是否已压缩,例如 gzip 。
在我的特殊情况下,这已经足够了。但这仅仅是协议的一小部分,并且它一般不适合。虽然,也许它可以为你工作。
因此,您需要先从服务器获取所有标题,然后读取每一行,直到出现空行。获得标题后,您可以使用该信息来决定如何阅读响应正文。基本上,您希望实现一个简单的HTTP客户端,至少应该实现HTTP协议的基本功能。
这是很多工作,我想如果你可以安装OpenSSL,很可能你可以使用 libcurl ,但我不确定,因为我从未使用过嵌入式Linux。
注意:如果您了解c字符串的工作方式,则不会使用strcat()
。
答案 1 :(得分:2)
两件事:
除非您要实现HTTP 1.1规范的所有要求,否则不要声明HTTP / 1.1支持。
你不能认为你第一次打电话给SSL_read
会得到你的标题,而你的下一个会给你身体。您必须实际实现HTTP协议。 SSL不了解HTTP,也不知道标题或正文是什么或如何阅读它们。
老实说,你的协议知识水平表明你已经超出了你的深度。您真的应该找到一个满足您需求的现有HTTP客户端实现,而不是尝试自己推出。即使每次测试时它都会发生作用,但仍然存在大量微妙的错误,而且每一个对此都不熟悉的人几乎都是这样做的。
例如:
bytes = SSL_read(ssl, buffer, sizeof(buffer));
buffer[bytes] = 0;
如果第一行后bytes == sizeof(buffer)
怎么办?你的第二行将写入缓冲区的末尾。