Lwip-2.1.3源码中提供了http client接口,但是默认只有GET方法,如何基于源码最小改动添加POST方法?
lwip/src/apps/http/http_client.c
GET 和 POST 报文区别这里不表。
1、http_client.c中GET的基础报文:
/* GET request basic */
#define HTTPC_REQ_11 "GET %s HTTP/1.1\r\n" /* URI */\
"User-Agent: %s\r\n" /* User-Agent */ \
"Accept: */*\r\n" \
"Connection: Close\r\n" /* we don't support persistent connections, yet */ \
"\r\n"
#define HTTPC_REQ_11_FORMAT(uri) HTTPC_REQ_11, uri, HTTPC_CLIENT_AGENT
/* GET request with host */
#define HTTPC_REQ_11_HOST "GET %s HTTP/1.1\r\n" /* URI */\
"User-Agent: %s\r\n" /* User-Agent */ \
"Accept: */*\r\n" \
"Host: %s\r\n" /* server name */ \
"Connection: Close\r\n" /* we don't support persistent connections, yet */ \
"\r\n"
#define HTTPC_REQ_11_HOST_FORMAT(uri, srv_name) HTTPC_REQ_11_HOST, uri, HTTPC_CLIENT_AGENT, srv_name
/* GET request with proxy */
#define HTTPC_REQ_11_PROXY "GET http://%s%s HTTP/1.1\r\n" /* HOST, URI */\
"User-Agent: %s\r\n" /* User-Agent */ \
"Accept: */*\r\n" \
"Host: %s\r\n" /* server name */ \
"Connection: Close\r\n" /* we don't support persistent connections, yet */ \
"\r\n"
#define HTTPC_REQ_11_PROXY_FORMAT(host, uri, srv_name) HTTPC_REQ_11_PROXY, host, uri, HTTPC_CLIENT_AGENT, srv_name
/* GET request with proxy (non-default server port) */
#define HTTPC_REQ_11_PROXY_PORT "GET http://%s:%d%s HTTP/1.1\r\n" /* HOST, host-port, URI */\
"User-Agent: %s\r\n" /* User-Agent */ \
"Accept: */*\r\n" \
"Host: %s\r\n" /* server name */ \
"Connection: Close\r\n" /* we don't support persistent connections, yet */ \
"\r\n"
#define HTTPC_REQ_11_PROXY_PORT_FORMAT(host, host_port, uri, srv_name) HTTPC_REQ_11_PROXY_PORT, host, host_port, uri, HTTPC_CLIENT_AGENT, srv_name
类比写出 POST的基础报文格式(post标准报文格式自行查找资料):
/* POST request basic */
#define HTTPC_REQ_11_POST "POST %s HTTP/1.1\r\n" /* URI */\
"User-Agent: %s\r\n" /* User-Agent */ \
"Accept: */*\r\n" \
"Content-Type: application/x-www-form-urlencoded\r\n" \
"Content-Length: %d\r\n" \
"Connection: Close\r\n" /* we don't support persistent connections, yet */\
"\r\n" \
"%s"
#define HTTPC_REQ_11_FORMAT_POST(uri, arglen, args) HTTPC_REQ_11_POST, uri, HTTPC_CLIENT_AGENT, arglen, args
/* POST request with host */
#define HTTPC_REQ_11_HOST_POST "POST %s HTTP/1.1\r\n" /* URI */\
"User-Agent: %s\r\n" /* User-Agent */ \
"Accept: */*\r\n" \
"Host: %s\r\n" /* server name */ \
"Content-Type: application/x-www-form-urlencoded\r\n" \
"Content-Length: %d\r\n" \
"Connection: Close\r\n" /* we don't support persistent connections, yet */ \
"\r\n" \
"%s"
#define HTTPC_REQ_11_HOST_FORMAT_POST(uri, srv_name, arglen, args) HTTPC_REQ_11_HOST, uri, HTTPC_CLIENT_AGENT, srv_name, arglen, args
Content-Type可以有多种,可以根据不同需求调整。
这里没有写出post使用代理的报文(因为没用到),需要的话可以参照GET的代理报文。
2、生成完整报文函数:
httpclient模块使用 httpc_create_request_string 函数生成 GET 报文,所以可以从这个函数着手。
static int
httpc_create_request_string(const httpc_connection_t *settings, const char* server_name, int server_port, const char* uri,
int use_host, char *buffer, size_t buffer_size)
{
if (settings->use_proxy) {
LWIP_ASSERT("server_name != NULL", server_name != NULL);
if (server_port != HTTP_DEFAULT_PORT) {
return snprintf(buffer, buffer_size, HTTPC_REQ_11_PROXY_PORT_FORMAT(server_name, server_port, uri, server_name));
} else {
return snprintf(buffer, buffer_size, HTTPC_REQ_11_PROXY_FORMAT(server_name, uri, server_name));
}
} else if (use_host) {
LWIP_ASSERT("server_name != NULL", server_name != NULL);
return snprintf(buffer, buffer_size, HTTPC_REQ_11_HOST_FORMAT(uri, server_name));
} else {
return snprintf(buffer, buffer_size, HTTPC_REQ_11_FORMAT(uri));
}
}
这里先撇开代理不看,添加使用post的分支:
static int
httpc_create_request_string(const httpc_connection_t *settings, const char* server_name, int server_port, const char* uri,
int use_host, char *buffer, size_t buffer_size)
{
if (settings->use_proxy) {
LWIP_ASSERT("server_name != NULL", server_name != NULL);
if (server_port != HTTP_DEFAULT_PORT) {
return snprintf(buffer, buffer_size, HTTPC_REQ_11_PROXY_PORT_FORMAT(server_name, server_port, uri, server_name));
} else {
return snprintf(buffer, buffer_size, HTTPC_REQ_11_PROXY_FORMAT(server_name, uri, server_name));
}
} else if (use_host) {
LWIP_ASSERT("server_name != NULL", server_name != NULL);
if (USE_POST){
return snprintf(buffer, buffer_size, HTTPC_REQ_11_HOST_FORMAT_POST(uri, server_name, ARGLENS, ARGS));
}
return snprintf(buffer, buffer_size, HTTPC_REQ_11_HOST_FORMAT(uri, server_name));
} else {
if (USE_POST){
return snprintf(buffer, buffer_size, HTTPC_REQ_11_FORMAT_POST(uri, ARGLENS, ARGS));
}
return snprintf(buffer, buffer_size, HTTPC_REQ_11_FORMAT(uri));
}
}
思考:
USE_POST, ARGLENS, ARGS 如何来?
3、修改 httpc_connection_t
从函数中可以看出判断是否使用代理使用的是 settings->use_proxy 。
settings类型:
typedef struct _httpc_connection {
ip_addr_t proxy_addr;
u16_t proxy_port;
u8_t use_proxy;
/* @todo: add username:pass? */
#if LWIP_ALTCP
altcp_allocator_t *altcp_allocator;
#endif
/* this callback is called when the transfer is finished (or aborted) */
httpc_result_fn result_fn;
/* this callback is called after receiving the http headers
It can abort the connection by returning != ERR_OK */
httpc_headers_done_fn headers_done_fn;
} httpc_connection_t;
同样可以在该结构体中添加触发使用 post 的成员变量,类似如下:
typedef struct _httpc_connection {
ip_addr_t proxy_addr;
u16_t proxy_port;
u8_t use_proxy;
/* @todo: add username:pass? */
#if LWIP_ALTCP
altcp_allocator_t *altcp_allocator;
#endif
/* this callback is called when the transfer is finished (or aborted) */
httpc_result_fn result_fn;
/* this callback is called after receiving the http headers
It can abort the connection by returning != ERR_OK */
httpc_headers_done_fn headers_done_fn;
u8_t use_post;
u8_t arglen;
u8_t* args;
} httpc_connection_t;
总结:
修改后的_httpc_connection 定义如下:
typedef struct _httpc_connection {
ip_addr_t proxy_addr;
u16_t proxy_port;
u8_t use_proxy;
/* @todo: add username:pass? */
#if LWIP_ALTCP
altcp_allocator_t *altcp_allocator;
#endif
/* this callback is called when the transfer is finished (or aborted) */
httpc_result_fn result_fn;
/* this callback is called after receiving the http headers
It can abort the connection by returning != ERR_OK */
httpc_headers_done_fn headers_done_fn;
u8_t use_post;
u8_t arglen;
u8_t* args;
} httpc_connection_t;
修改后的httpc_create_request_string 函数如下:
static int
httpc_create_request_string(const httpc_connection_t *settings, const char* server_name, int server_port, const char* uri,
int use_host, char *buffer, size_t buffer_size)
{
if (settings->use_proxy) {
LWIP_ASSERT("server_name != NULL", server_name != NULL);
if (server_port != HTTP_DEFAULT_PORT) {
return snprintf(buffer, buffer_size, HTTPC_REQ_11_PROXY_PORT_FORMAT(server_name, server_port, uri, server_name));
} else {
return snprintf(buffer, buffer_size, HTTPC_REQ_11_PROXY_FORMAT(server_name, uri, server_name));
}
} else if (use_host) {
LWIP_ASSERT("server_name != NULL", server_name != NULL);
if (settings->use_post){
return snprintf(buffer, buffer_size, HTTPC_REQ_11_HOST_FORMAT_POST(uri, server_name, settings->arglen, settings->args));
}
return snprintf(buffer, buffer_size, HTTPC_REQ_11_HOST_FORMAT(uri, server_name));
} else {
if (settings->use_post){
return snprintf(buffer, buffer_size, HTTPC_REQ_11_FORMAT_POST(uri, settings->arglen, settings->args));
}
return snprintf(buffer, buffer_size, HTTPC_REQ_11_FORMAT(uri));
}
}
使用:
static
err_t http_rev_func(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
LWIP_PLATFORM_DIAG(("this is http_rev_func\r\n"));
pbuf_free(p);
return ERR_OK;
}
httpc_connection_t setting = { .use_proxy = 0,
.result_fn = NULL,
.headers_done_fn = NULL};
// 使用 GET 方法
httpc_get_file(&ser_ipaddr, 8080,"/index.html", NULL, http_rev_func, NULL, NULL);
// 使用 POST 方法
setting.use_post = 1;
setting.arglen = 0;
setting.args = NULL;
httpc_get_file(&ser_ipaddr, 8080,"/index.html", &setting , http_rev_func, NULL, NULL);