纯C实现https请求,跨平台的httpclient库,体积仅100K

最近在用FreeBasic写跨平台的管理系统,自然少不了REST API模块,但是https请求绕不开加密库,例如curl+openssl+zlib的解决方案,如果是静态编译,空程序就需要2.5M,动态则需要带几个dll体积更大,而实际上我们仅仅只需要https。。

找了一圈,最终发现有个停更七年的开源项目httpclient,项目地址在

httpclient: 纯C语言写的http client,支持 https,支持GET POST, 不依赖其他库

ssl/tls库使用krypton(https://github.com/cesanta/krypton)

解析部分使用http_parser(https://github.com/nodejs/http-parser)

作者把这两个项目文件都拷贝到项目里了,krypton你可能没听说过,但是他的另外一个作品是mongoose。

但是使用MSSY2下面的mingw编译如果krypton没编译通过,提示错误是

krypton.c:697:20: error: invalid suffix "i64" on integer constant
  697 | #define COMP_RADIX 4294967296i64

其实问题很简单,i64这个后缀改成LL就行

#define COMP_RADIX 4294967296i64
//修改为
#define COMP_RADIX 4294967296LL

具体原因可以参考C++ Integer Constants | Microsoft Learn

另外krypton库作者估计没用win下面的mingw32/64编译器,只是兼容了msvc的情况,需要做一下调整,不然无法编译测试程序:

#ifdef _MSC_VER
#include <winsock2.h>
#include <windows.h>
#define __unused
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
typedef int int32_t;
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef unsigned char uint8_t;
typedef unsigned long uintptr_t;
typedef long ssize_t;
#define __func__ ""
#define __packed
#ifndef alloca
#define alloca(x) _alloca(x)
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif
#define SOCKET_ERRNO WSAGetLastError()
#pragma comment(lib, "ws2_32.lib")  // Linking with winsock library
#else                               /* _MSC_VER */
//需要插入以下代码
#ifdef _WIN32
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")  // Linking with winsock library
#endif
//插入完毕
#include <stdint.h>
#include <unistd.h>
#define __packed __attribute__((packed))
#define SOCKET_ERRNO errno
#endif

这个库很有价值,体积确实很小,执行编译:

gcc -c *.c -lws2_32
ar -rc libhttpclient.a http_parser.o krypton.o http.o

编译完成可以得到libhttpclient.a,体积只有122k,将文件拷贝到Compile\expand\lib\win32文件夹

下面继续祭出fbfrog.exe,拷贝default.h到根目录,执行

# fbfrog.exe http.h -o httpclient.bi -inclib ws2_32 -inclib httpclient
#pragma once

#inclib "ws2_32"
#inclib "httpclient"

extern "C"

#define _HTTP_H_
#define HTTP_API
const FT_SUPPORT_HTTPS = 1

type http_request_method_e as long
enum
	M_GET = 0
	M_POST
	M_HEAD
end enum

type http_error_e as long
enum
	ERR_OK = 0
	ERR_INVALID_PARAM
	ERR_OUT_MEMORY
	ERR_OPEN_FILE
	ERR_PARSE_REP
	ERR_URL_INVALID
	ERR_URL_INVALID_PROTO
	ERR_URL_INVALID_HOST
	ERR_URL_INVALID_IP
	ERR_URL_RESOLVED_HOST
	ERR_SOCKET_CREATE
	ERR_SOCKET_SET_OPT
	ERR_SOCKET_NOBLOCKING
	ERR_SOCKET_CONNECT
	ERR_SOCKET_CONNECT_TIMEOUT
	ERR_SOCKET_SELECT
	ERR_SOCKET_WRITE
	ERR_SOCKET_READ
	ERR_SOCKET_TIMEOUT
	ERR_SOCKET_CLOSED
	ERR_SOCKET_GET_OPT
	ERR_SSL_CREATE_CTX
	ERR_SSL_CREATE_SSL
	ERR_SSL_SET_FD
	ERR_SSL_CONNECT
	ERR_SSL_WRITE
	ERR_SSL_READ
end enum

type data_recv_cb_t as function(byval http as ft_http_client_t ptr, byval data as const zstring ptr, byval size as long, byval total as long, byval user as any ptr) as long
declare function ft_http_init() as long
declare sub ft_http_deinit()
declare function ft_http_new() as ft_http_client_t ptr
declare sub ft_http_destroy(byval http as ft_http_client_t ptr)
declare function ft_http_get_error_code(byval http as ft_http_client_t ptr) as long
declare function ft_http_get_status_code(byval http as ft_http_client_t ptr) as long
declare function ft_http_set_timeout(byval http as ft_http_client_t ptr, byval timeout as long) as long
declare function ft_http_sync_request(byval http as ft_http_client_t ptr, byval url as const zstring ptr, byval m_ as http_request_method_e) as const zstring ptr
declare function ft_http_sync_download_file(byval http as ft_http_client_t ptr, byval url as const zstring ptr, byval filepath as const zstring ptr) as long
declare function ft_http_cancel_request(byval http as ft_http_client_t ptr) as long
declare function ft_http_wait_done(byval http as ft_http_client_t ptr) as long
declare function ft_http_set_data_recv_cb(byval http as ft_http_client_t ptr, byval cb as data_recv_cb_t, byval user as any ptr) as long
declare function ft_http_exit(byval http as ft_http_client_t ptr) as long
declare function ft_http_sync_post_file(byval http as ft_http_client_t ptr, byval url as const zstring ptr, byval filepath as const zstring ptr) as const zstring ptr

end extern

粗略看一下代码,会发现ft_http_client_t结构体并没有翻译出来,这个结构体比较复杂,但是我们可以申明一个type ft_http_client_t as any ptr,然后把ft_http_client_t ptr全部替换为ft_http_client_t

#pragma once

#inclib "ws2_32"
#inclib "httpclient"

extern "C"

#define _HTTP_H_
#define HTTP_API
const FT_SUPPORT_HTTPS = 1

type http_request_method_e as long
enum
	M_GET = 0
	M_POST
	M_HEAD
end enum

type http_error_e as long
enum
	ERR_OK = 0
	ERR_INVALID_PARAM
	ERR_OUT_MEMORY
	ERR_OPEN_FILE
	ERR_PARSE_REP
	ERR_URL_INVALID
	ERR_URL_INVALID_PROTO
	ERR_URL_INVALID_HOST
	ERR_URL_INVALID_IP
	ERR_URL_RESOLVED_HOST
	ERR_SOCKET_CREATE
	ERR_SOCKET_SET_OPT
	ERR_SOCKET_NOBLOCKING
	ERR_SOCKET_CONNECT
	ERR_SOCKET_CONNECT_TIMEOUT
	ERR_SOCKET_SELECT
	ERR_SOCKET_WRITE
	ERR_SOCKET_READ
	ERR_SOCKET_TIMEOUT
	ERR_SOCKET_CLOSED
	ERR_SOCKET_GET_OPT
	ERR_SSL_CREATE_CTX
	ERR_SSL_CREATE_SSL
	ERR_SSL_SET_FD
	ERR_SSL_CONNECT
	ERR_SSL_WRITE
	ERR_SSL_READ
end enum

type ft_http_client_t as any ptr

type data_recv_cb_t as function(byval http as ft_http_client_t, byval data as const zstring ptr, byval size as long, byval total as long, byval user as any ptr) as long
declare function ft_http_init() as long
declare sub ft_http_deinit()
declare function ft_http_new() as ft_http_client_t
declare sub ft_http_destroy(byval http as ft_http_client_t)
declare function ft_http_get_error_code(byval http as ft_http_client_t) as long
declare function ft_http_get_status_code(byval http as ft_http_client_t) as long
declare function ft_http_set_timeout(byval http as ft_http_client_t, byval timeout as long) as long
declare function ft_http_sync_request(byval http as ft_http_client_t, byval url as const zstring ptr, byval m_ as http_request_method_e) as const zstring ptr
declare function ft_http_sync_download_file(byval http as ft_http_client_t, byval url as const zstring ptr, byval filepath as const zstring ptr) as long
declare function ft_http_cancel_request(byval http as ft_http_client_t) as long
declare function ft_http_wait_done(byval http as ft_http_client_t) as long
declare function ft_http_set_data_recv_cb(byval http as ft_http_client_t, byval cb as data_recv_cb_t, byval user as any ptr) as long
declare function ft_http_exit(byval http as ft_http_client_t) as long
declare function ft_http_sync_post_file(byval http as ft_http_client_t, byval url as const zstring ptr, byval filepath as const zstring ptr) as const zstring ptr

end extern

 把httpclient.bi拷贝到Compile\expand\inc文件夹

创建一个标准的exe的项目,在起始模块加入#include Once "httpclient.bi"

在Form1上加入

Sub Form1_Shown(hWndForm As hWnd,UserData As Integer)
   ft_http_init()
   Dim ft As ft_http_client_t = ft_http_new()
   ft_http_sync_download_file(ft, "https://www.taobao.com/", "index.html")
End Sub

运行后在release文件夹下面会有index.html文件,大小约为86K

如果不支持https的话,淘宝的返回的将会是

HTTP/1.1 400 Bad Request
Server: Tengine
Date: Mon, 08 May 2023 22:06:32 GMT
Content-Type: text/html
Content-Length: 263
Connection: close
Via: cache2.cn5570[,0]
Timing-Allow-Origin: *
EagleId: 0000000016835835920664266e

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>
<body>
<h1>400 Bad Request</h1>
<p>The plain HTTP request was sent to HTTPS port.<hr/>Powered by Tengine</body>
</html>

证明组件效果很好。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值