今天抽了点时间看了一下 mongoose的源码, github 地址,发现跟以前公司内部使用的不太一样,这里正好利用其 http server 例子来看一下。以前的 http message 结构体是这样的:
cpp
/* HTTP message */
struct http_message {
struct mg_str message; /* Whole message: request line + headers + body */
struct mg_str body; /* Message body. 0-length for requests with no body */
/* HTTP Request line (or HTTP response line) */
struct mg_str method; /* "GET" */
struct mg_str uri; /* "/my_file.html" */
struct mg_str proto; /* "HTTP/1.1" -- for both request and response */
/* For responses, code and response status message are set */
int resp_code;
struct mg_str resp_status_msg;
/*
* Query-string part of the URI. For example, for HTTP request
* GET /foo/bar?param1=val1¶m2=val2
* | uri | query_string |
*
* Note that question mark character doesn't belong neither to the uri,
* nor to the query_string
*/
struct mg_str query_string;
/* Headers */
struct mg_str header_names[MG_MAX_HTTP_HEADERS];
struct mg_str header_values[MG_MAX_HTTP_HEADERS];
};
github 上的源码的http message 结构体是这样的:
cpp
struct mg_http_message {
struct mg_str method, uri, query, proto; // Request/response line
struct mg_http_header headers[MG_MAX_HTTP_HEADERS]; // Headers
struct mg_str body; // Body
struct mg_str head; // Request + headers
struct mg_str chunk; // Chunk for chunked encoding, or partial body
struct mg_str message; // Request + headers + body
};
很明显现在的头部消息使用了 mg_http_header 结构体,它的定义是这样的:
cpp
struct mg_http_header {
struct mg_str name; // Header name
struct mg_str value; // Header value
};
今天使用的是mongoose 自带的例子,位于源码目录的 examples/http-server 下的 main.c,因为我使用了c++的,所以我自己创建了目录 myExample/http_server,把它的 main.c 换成了 main.cpp,makefile 使用自己的,在编译的时候解决一下编译问题即可。
自己写的Makefile
#中间文件存放目录,如.o 和 .d 文件
COMPILE_DIR = compile
BIN_DIR = bin
# 可编译arm版本
#CROSS = arm-himix200-linux-
CC = gcc
CPP = $(CROSS)g++ -std=c++11
CFLAGS = -Werror -g
CFLAGS += -I../../
CPP_SRCS = $(wildcard *.cpp)
CPP_OBJS = $(patsubst %.cpp, $(COMPILE_DIR)/%.o, $(CPP_SRCS))
CPP_DEP = $(patsubst %.cpp, $(COMPILE_DIR)/%.cpp.d, $(CPP_SRCS))
OBJS = $(CPP_OBJS) $(C_OBJS)
DEP_ALL = $(CPP_DEP) $(C_DEP)
$(shell if [ ! -d $(COMPILE_DIR) ]; then mkdir $(COMPILE_DIR); fi)
$(shell if [ ! -d $(BIN_DIR) ]; then mkdir $(BIN_DIR); fi)
BIN =
ifeq ($(target), ) #如果是空的
BIN = httpServer
else
BIN := $(target)
endif
TARGET=$(BIN_DIR)/$(BIN)
all: $(TARGET)
-include $(DEP_ALL)
$(TARGET): $(OBJS) $(COMPILE_DIR)/mongoose.o
$(CPP) $(CFLAGS) $^ -o $@
$(COMPILE_DIR)/%.o: %.cpp $(COMPILE_DIR)/%.cpp.d
$(CPP) $(CFLAGS) -c $< -o $@
$(COMPILE_DIR)/%.cpp.d: %.cpp
$(CPP) $(CFLAGS) -MM -E -c $< -o $@
@sed 's/.*\.o/$(subst /,\/,$(dir $@))&/g' $@ > $@.tmp
@mv $@.tmp $@
$(COMPILE_DIR)/mongoose.o: ../../mongoose.c
$(CC) $(CFLAGS) -c $< -o $@
$(COMPILE_DIR)/%.c.d: %.c
$(CC) $(CFLAGS) -MM -E -c $< -o $@
@sed 's/.*\.o/$(subst /,\/,$(dir $@))&/g' $@ > $@.tmp
@mv $@.tmp $@
.PHONY: clean
clean:
rm -rf $(COMPILE_DIR) $(BIN_DIR)
同时把main.cpp里的书写格式也改了,还是习惯一行行的看代码:
cpp
// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved
#include <signal.h>
#include "mongoose.h"
static int s_debug_level = MG_LL_INFO;
static const char *s_root_dir = ".";
static const char *s_listening_address = "http://0.0.0.0:8190";
static const char *s_enable_hexdump = "no";
static const char *s_ssi_pattern = "#.html";
// Handle interrupts, like Ctrl-C
static int s_signo;
static void signal_handler(int signo)
{
s_signo = signo;
}
// Event handler for the listening connection.
// Simply serve static files from `s_root_dir`
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data)
{
if (ev == MG_EV_HTTP_MSG)
{
struct mg_http_message *hm = (mg_http_message *)ev_data, tmp = {0};
struct mg_str unknown = mg_str_n("?", 1), *cl;
struct mg_http_serve_opts opts = {0};
//不能用ptr直接打印,需要根据len
MG_INFO(("method(%d): %s, uri(%d): %s, query(%d): %s, proto(%d): %s", hm->method.len, hm->method.ptr, hm->uri.len, hm->uri.ptr, hm->query.len, hm->query.ptr,
hm->proto.len, hm->proto.ptr));
MG_INFO(("method ptr: %p, uri ptr: %p, query ptr: %p, proto ptr: %p\n", hm->method.ptr, hm->uri.ptr, hm->query.ptr, hm->proto.ptr));
//设置目录起点
opts.root_dir = s_root_dir;
opts.ssi_pattern = s_ssi_pattern;
mg_http_serve_dir(c, hm, &opts);
mg_http_parse((char *) c->send.buf, c->send.len, &tmp);
cl = mg_http_get_header(&tmp, "Content-Length");
if (cl == NULL)
{
cl = &unknown;
}
MG_INFO(("%.*s %.*s %.*s %.*s", (int) hm->method.len, hm->method.ptr,
(int) hm->uri.len, hm->uri.ptr, (int) tmp.uri.len, tmp.uri.ptr,
(int) cl->len, cl->ptr));
}
(void) fn_data;
}
static void usage(const char *prog)
{
fprintf(stderr,
"Mongoose v.%s\n"
"Usage: %s OPTIONS\n"
" -H yes|no - enable traffic hexdump, default: '%s'\n"
" -S PAT - SSI filename pattern, default: '%s'\n"
" -d DIR - directory to serve, default: '%s'\n"
" -l ADDR - listening address, default: '%s'\n"
" -v LEVEL - debug level, from 0 to 4, default: %d\n",
MG_VERSION, prog, s_enable_hexdump, s_ssi_pattern, s_root_dir,
s_listening_address, s_debug_level);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
char path[MG_PATH_MAX] = ".";
struct mg_mgr mgr;
struct mg_connection *c;
int i;
// Parse command-line flags
for (i = 1; i < argc; i++)
{
if (strcmp(argv[i], "-d") == 0)
{
s_root_dir = argv[++i];
}
else if (strcmp(argv[i], "-H") == 0)
{
s_enable_hexdump = argv[++i];
}
else if (strcmp(argv[i], "-S") == 0)
{
s_ssi_pattern = argv[++i];
}
else if (strcmp(argv[i], "-l") == 0)
{
s_listening_address = argv[++i];
}
else if (strcmp(argv[i], "-v") == 0)
{
s_debug_level = atoi(argv[++i]);
}
else
{
usage(argv[0]);
}
}
// Root directory must not contain double dots. Make it absolute
// Do the conversion only if the root dir spec does not contain overrides
if (strchr(s_root_dir, ',') == NULL)
{
realpath(s_root_dir, path);
s_root_dir = path;
}
// Initialise stuff
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
mg_log_set(s_debug_level);
mg_mgr_init(&mgr);
if ((c = mg_http_listen(&mgr, s_listening_address, cb, &mgr)) == NULL)
{
MG_ERROR(("Cannot listen on %s. Use http://ADDR:PORT or :PORT", s_listening_address));
exit(EXIT_FAILURE);
}
if (mg_casecmp(s_enable_hexdump, "yes") == 0)
{
c->is_hexdumping = 1;
}
// Start infinite event loop
MG_INFO(("Mongoose version : v%s", MG_VERSION));
MG_INFO(("Listening on : %s", s_listening_address));
MG_INFO(("Web root : [%s]", s_root_dir));
while (s_signo == 0)
{
mg_mgr_poll(&mgr, 1000);
}
mg_mgr_free(&mgr);
MG_INFO(("Exiting on signal %d", s_signo));
return 0;
}
在实际使用的时候需要注意 struct mg_str 结构体,如 mg_http_message 中的 method, uri, query, proto 等,代码里我只是好奇地想打印一下这些数据,然而结果并不是我想要的,如:
cpp
MG_INFO(("method(%d): %s, uri(%d): %s, query(%d): %s, proto(%d): %s", hm->method.len, hm->method.ptr, hm->uri.len, hm->uri.ptr, hm->query.len, hm->query.ptr,
hm->proto.len, hm->proto.ptr));
MG_INFO(("method ptr: %p, uri ptr: %p, query ptr: %p, proto ptr: %p\n", hm->method.ptr, hm->uri.ptr, hm->query.ptr, hm->proto.ptr));
打印 mg_str 的时候使用了其 ptr 指针,而打印出这些指针可以看到它们指向的是连续的一个地址:
[2023/08/10 14:52:21 510890 Info] main.cpp:32:cb method ptr: 0x1f30580, uri ptr: 0x1f30584, query ptr: 0x0, proto ptr: 0x1f30586
0x1f30580 + 3(method 的长度) + 1(一个空格) = 0x1f30584,0x1f30584 + 1(uri的长度)+1(一个空格) = 0x1f30586。
使用了 std::string
cpp
std::string method(hm->method.ptr, hm->method.len);
std::string uri(hm->uri.ptr, hm->uri.len);
std::string proto(hm->proto.ptr, hm->proto.len);
MG_INFO(("method: %s, uri: %s, proto: %s\n", method.c_str(), uri.c_str(), proto.c_str()));