Web服務器----mongoose

概述:
 簡介:
    Mongoose是c語言寫成的網絡庫。 它爲TCP、UDP、HTTP、WebSocket、CoAP、MQTT實現了事件驅動型的非阻塞api。其具備如下特性:
     跨平臺:可在linux/unix  macos QNX eCos Windows Android Iphone FreeRtos上運行
     原生支持PicoTCP的嵌入式tcp/ip協議棧,支持LWIP嵌入式tcp/ip協議棧
      單線程,異步,非阻塞核心與簡單的基於事件的API
      可配置爲:
                純TCP,純UDP SSL/TLS但雙向的客戶端和服務器;HTTP,WebSocket,MQTT,CoAP,DNS的客戶端和服務器,同時可做爲異步dns解析器。
      在運行和佔用很小的內存,源代碼符合ISO C 和ISO c++
      同時僅僅複製mongoose.c mongoose.h到你的工程便可完成整合。
Mongoose設計思路:
    擁有3個基本類型的數據結構
1 struct mg_mgr;///事件管理器,保存全部的活動連接
2 struct mg_connection;///描述一個連接
3 struct mbuf;///接收和發送的數據
 
       一個連接能夠是listening(監聽),outbound (出站)或者inbound(入站)。 outbound (出站)連接可經過調用mg_connect()產生。listening(監聽)連接可經過調用mg_bind()產生。(入站)inbound連接是由listening(監聽)連接所收到的連接。每一個連接都使用struct mg_connection進行描述,此結構中有,socket,事件處理函數,發送/接收緩衝區,以及其餘標誌。
    使用mongoose的應用程序應該遵循事件驅動的標準模式:
複製代碼
1 struct mg_mgr mgr;
2 mg_mgr_init(&mgr, NULL);////建立並初始化事件管理器
3 struct mg_connection *c = mg_bind(&mgr, "80", ev_handler_function);
4 mg_set_protocol_http_websocket(c);///建立連接,上面2行代碼是服務器應用程序建立的監聽連接
5 for (;;) {
6    mg_mgr_poll(&mgr, 1000);
7  }///經過調用mg_mgr_pool()建立事件循環。
複製代碼
 
    mg_mgr_poll()遍歷全部的套接字,接受新連接,發送,接收數據,關閉連接。併爲各自的事件調用事件處理函數。有關完整實例,可參考TCP echo server的使用實例。
內存緩衝區:
    每一個連接都有接收和發送的數據緩衝區: struct mg_connection::recv_mbuf, struct mg_connection::send_mbuf .當接收到數據的時候Mongoose將數據追加到recv_mbuf,而且觸發MG_EV_RECV事件。用戶可調用 mg_send(),mg_printf() 這些輸出函數數據追加到send_mbuf中併發送回去。當mongoose成功地向socket寫入數據的時候,mongoose會將此數據從send_mbuf中丟棄,併發送MG_EV_SEND事件。當連接關閉的時候,發送MG_EV_CLOSE事件。
事件處理函數:
        每一個連接都有其與之相關的事件處理函數。此事件處理函數由用戶本身實現。事件處理函數是mongoose程序的關鍵,由於其定義了應用程序的具體行爲。事件處理函數以下:
複製代碼
 1 static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
 2   switch (ev) {
 3     /* Event handler code that defines behavior of the connection */
 4     ...
 5   }
 6 }
 7 ////////////////////////////////////解讀////////////////////////////////////////////
 8 struct mg_connection *nc;///鏈接所接收到的事件
 9 int ev;///事件編號,可在mongoose.h中找到。當(入站)連接收到數據的時候,ev=MG_EV_RECV
10 void *ev_data;///此指針指向事件特定數據,對於不一樣事件有不一樣含義。每一個事件都描述了ev_data的具體含義。特定協議的事件ev_data一般指向包含協議特定信息的結構。
11 /*
12  MG_EV_RECV事件:ev_data是一個int *,保存從連接中接收,並保存到接收IO緩衝區中字節數
13  struct mg_connection 有應用程序特定數據佔位符void *user_data。
14  mongoose不適用指針,事件處理函數user_data這個指針,事件處理函數可在此存儲任何信息。
15 */
複製代碼

事件:php

    mongoose接收連接,讀寫數據併爲每一個連接調用指定的事件處理函數。典型的事件序列:
    出站連接: MG_EV_CONNECT->(MG_EV_RECV,MG_EV_SEND,MG_EV_POLL...)->MG_EV_CLOSE
    入站連接: MG_EV_ACCEPT->(MG_EV_RECV,MG_EV_SEND,MG_EV_POLL...)->MG_EV_CLOSE
    下面是mongoose觸發的核心事件列表:(除了核心事件外,每一個協議都會觸發協議的特定事件)
    MG_EV_ACCEPT:監聽連接接收到一個新的連接,void *ev_data 是遠程連接的union socket_addres
    MG_EV_CONNECT:mg_connect()建立一個出站連接(不管mg_connect()調用是否成功),void *ev_data 是 int *success. success=0,連接創建成功,不然連接創建失敗,幷包含一個錯誤碼。可查看mg_connect_opt()查看 代碼實例。
    MG_EV_RECV:接收到新數據,並將數據追加到recv_mbuf.void * ev_data是 int *num_received_bytes.一般狀況下事件處理程序應該在nc->recv_mbuf()檢查接受到的數據,經過調用mbuf_remove()丟棄處理後的數據,必要時設置連接標誌nc->flags(查看struct mg_connection).使用mg_send()向遠程連接點發送數據。
    mongoose使用realloc()來擴展接收緩衝區,從接收緩衝區的開頭丟棄處理過的數據是用戶的責任。注意上面的mbuf_remove().
  MG_EV_SEND:mongoos向遠程鏈接點寫入數據,並將mg_connectiong::send_mbuf中數據丟棄。void *ev_data是int *num_sent_bytes即發送的字節數。mongoose輸出函數僅僅是將數據追加到sned_mbuf,不作任何socket寫操做。實際的IO寫操做是由mg_mgr_pool()完成,MG_EV_SEND僅僅是一個關於IO操做已經完成的通知。 
  MG_EV_POLL:將每次調用mg_mgr_poll()發送到每一個連接。此事件可作任何事情。例如檢測某個超時連接是否關閉,或者發送心跳。
  MG_EV_TIMER:  向某個調用mg_set_timer()的連接發送   
連接flags:
        每一個連接都有flasg位域。有些flags是由mongoose設置的,例如若是用戶使用udp://1.2.3.4:5678建立一個出站的UDP連接。mongoose會爲此連接設置MG_F_UDP標記。其餘標誌只能由用戶事件處理程序設置,告訴mongoose作何種操做。下面是由事件處理程序設置的連接flags列表:
     MG_F_FINISHED_SENDING_DATA:告訴mongoose全部數據已經追加到send_mbuf,只要mongoose將數據寫入socket,此連接就會關閉。
     MG_F_BUFFER_BUT_DONT_SEND:告訴mongoose追加 數據到send_mbuf,但數據要立刻發送,由於此數據稍後會被修改。而後經過清除 MG_F_BUFFER_BUT_DONT_SEND標誌將數據發送出去。
     MG_F_CLOSE_IMMEDIATELY:告訴mongoose當即關閉連接 ,一般在產生錯誤後發送此事件。
      MG_USER_1,MG_USER_2,MG_USER_3,MG_USER_4:開發者可用它來存儲特定應用程序的狀態
下面是由mongoose設置的flags:
     MG_F_SSL_HANDSHAKE_DONE:僅使用ssl,在ssl握手完成時設置
     MG_F_CONNECTING:在mg_connect()調用後連接處於連接狀態但未完成連接時設置。
    MG_F_LISTENING:設置全部監聽連接
   MG_F_UDP:連接是udp時設置。   
   MG_F_WEBSOCKET:連接是websocket時設置。
   MG_F_WEBSOCKET_NO_DEFRAG:若是用戶想關閉websocket的自動幀碎片整理功能,則由用戶設置此標記。
編譯選項:
    mongoose源代在一個.c文件中,此文件包含全部受支持協議(模塊)的功能。能夠在編譯時設置預處理器編制來禁用模塊,以減小可執行文件的大小。一些預處理器標誌能夠調整mongoose的內部參數。在編譯期間可以使用-D<PREPROCESSOR_FLAG>設置編譯器選項。例如要禁用MQTT和CoAP,編譯應用程序my_app.c在linux下用:
 
啓用標誌
Flag 介紹
MG_ENABLE_SSL 啓用SSL/TLS支持(OpenSSL API)
MG_ENABLE_IPV6 啓用IPV6支持
MG_ENABLE_MQTT 啓用MQTT客戶端(默認狀況下設置爲0表示禁用)
MG_ENABLE_MQTT_BROKER 啓用MQTT broker
MG_ENABLE_DNS_SERVER 啓用DNS服務器
MG_ENABLE_COAP 啓用CoAP協議
MG_ENABLE_HTTP 啓用HTTP協議支持(默認狀況下,設置0表示禁用)
MG_ENABLE_HTTP_CGI 啓用CGI
MG_ENABLE_HTTP_SSI 啓用Server Side 
MG_ENABLE_HTTP_SSI_EXEC 啓用SSI exec操做
MG_ENABLE_HTTP_WEBDAV 啓用HTTP的WebDAV擴展
MG_ENABLE_HTTP_WEBSOCKET 啓用HTTP的WebSocket擴展,默認狀況下 設置0禁止
MG_ENABLE_BROADCAST 啓用mg_broadcast()API
MG_ENABLE_GETADDRINFO 啓用在mg_resolve2()中的getaddrinfo()
MG_ENABLE_THREADS 啓用在mg_start_thread()API
禁用標誌
MG_DISABLE_HTTP_DIGEST_AUTH 禁用HTTP摘要(MD5)受權支持
CS_DISABLE_SHA1 禁用WebSocket中的SHA1支持
CS_DISABLE_MD5 禁用HTTP鑑權中的MD5支持
MG_DISABLE_HTTP_KEEP_ALIVE 用於嵌入式系統節省資源
平臺相關選項(mongoose會盡量的檢測目標平臺,可是在某些狀況下,必須明確聲明目標平臺的部分特性)
MG_CC3200 爲TICC3200板子啓用工做區
MG_ESP8266 爲ESP8266板子添加RTOS_SDK來指定RTOS_SDK風格
可調整
MG_MALLOC,MG_CALLOC,MG_REALLOC,MG_FREE 容許使用開發者自定義的內存分配處理函數
-DMG_MALLCO=my_malloc
MG_USE_READ_WRITE 設置後,將recv的調用替換爲read,send。從而容許向事件管理器添加任何類型的文件描述符(文件,串行設備)
MG_SSL_CRYPTO_MODERN,MG_SSL_CRYPTO_OLD 選擇modern或者old密碼替換默認的Intermediate密碼。在下面網址查看各類密碼形式的詳細信息
https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_configurations
MG_USER_FILE_FUNCTIONS 容許開發者經過定義本身的mg_stat,mg_fopen,mg_open,mg_fread,mg_fwrite去自定義文件操做函數
實例:TCP echo server
    複製mongoose.c mongoose.h到你的工程;在my_app.c中使用mongoose的API寫代碼。編譯代碼
$$cc my_app.c mongoose.c
複製代碼
 1 /********************my_app.c*******************/
 2 #include "mongoose.h"  // Include Mongoose API definitions
 3 // Define an event handler function
 4 static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
 5   struct mbuf *io = &nc->recv_mbuf;
 6   switch (ev) {
 7     case MG_EV_RECV:
 8       // This event handler implements simple TCP echo server
 9       mg_send(nc, io->buf, io->len);  // Echo received data back
10       mbuf_remove(io, io->len);      // Discard data from recv buffer
11       break;
12     default:
13       break;
14   }
15 }
16 
17 int main(void) {
18   struct mg_mgr mgr;
19   mg_mgr_init(&mgr, NULL);  // Initialize event manager object
20   // Note that many connections can be added to a single event manager
21   // Connections can be created at any point, e.g. in event handler function
22   mg_bind(&mgr, "1234", ev_handler);  // Create listening connection and add it to the event manager
23   for (;;) {  // Start infinite event loop
24     mg_mgr_poll(&mgr, 1000);
25   }
26   mg_mgr_free(&mgr);
27   return 0;
28 }
複製代碼
 
HTTP:
實例一:HTTP server
        1,使用mg_bind()或者mg_bind_opt()建立立監聽連接
        2.調用mg_set_protocol_http_websocket()去監聽全部連接。此函數附加了一個內置的http事件處理事件,用於解析並觸發特定的HTTP事件。例如當HTTP請求被徹底緩衝時,內置的HTPP處理程序將解析請求並使用MG_EV_HTTP_REQUEST事件調用用戶定義的事件處理函數,並將HTTP請求解析爲事件數據。
        3.建立事件處理函數。事件處理程序接收全部的事件(低級別TCP事件(MG_EV_RECV)和高級別HTTP事件(MG_EV_HTTP_REQUEST))。一般事件處理函數應該只處理高級別事件MG_EV_HTTP_REQUEST
    下面是HTTP server的實例代碼,其中省略了錯誤檢查
複製代碼
 1 // Copyright (c) 2015 Cesanta Software Limited
 2 // All rights reserved
 3 
 4 #include "mongoose.h"
 5 
 6 static const char *s_http_port = "8000";
 7 static struct mg_serve_http_opts s_http_server_opts;
 8 
 9 static void ev_handler(struct mg_connection *nc, int ev, void *p) {
10   if (ev == MG_EV_HTTP_REQUEST) {
11     mg_serve_http(nc, (struct http_message *) p, s_http_server_opts);
12   }
13 }
14 
15 int main(void) {
16   struct mg_mgr mgr;
17   struct mg_connection *nc;
18 
19   mg_mgr_init(&mgr, NULL);
20   printf("Starting web server on port %s\n", s_http_port);
21   nc = mg_bind(&mgr, s_http_port, ev_handler);
22   if (nc == NULL) {
23     printf("Failed to create listener\n");
24     return 1;
25   }
26 
27   // Set up HTTP server parameters
28   mg_set_protocol_http_websocket(nc);
29   s_http_server_opts.document_root = ".";  // Serve current directory
30   s_http_server_opts.enable_directory_listing = "yes";
31 
32   for (;;) {
33     mg_mgr_poll(&mgr, 1000);
34   }
35   mg_mgr_free(&mgr);
36 
37   return 0;
38 }
複製代碼

 

實例二:HTTP Client
        1:使用mg_connect_http()建立出站連接
        2:建立事件處理函數用於處理MG_EV_HTTP_REPLY事件
 
複製代碼
 1 /*
 2  * Copyright (c) 2014 Cesanta Software Limited
 3  * All rights reserved
 4  *
 5  * This program fetches HTTP URLs.
 6  */
 7 
 8 #include "mongoose.h"
 9 
10 static int s_exit_flag = 0;
11 static int s_show_headers = 0;
12 static const char *s_show_headers_opt = "--show-headers";
13 
14 static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
15   struct http_message *hm = (struct http_message *) ev_data;
16 
17   switch (ev) {
18     case MG_EV_CONNECT:
19       if (*(int *) ev_data != 0) {
20         fprintf(stderr, "connect() failed: %s\n", strerror(*(int *) ev_data));
21         s_exit_flag = 1;
22       }
23       break;
24     case MG_EV_HTTP_REPLY:
25       nc->flags |= MG_F_CLOSE_IMMEDIATELY;
26       if (s_show_headers) {
27         fwrite(hm->message.p, 1, hm->message.len, stdout);
28       } else {
29         fwrite(hm->body.p, 1, hm->body.len, stdout);
30       }
31       putchar('\n');
32       s_exit_flag = 1;
33       break;
34     case MG_EV_CLOSE:
35       if (s_exit_flag == 0) {
36         printf("Server closed connection\n");
37         s_exit_flag = 1;
38       }
39       break;
40     default:
41       break;
42   }
43 }
44 
45 int main(int argc, char *argv[]) {
46   struct mg_mgr mgr;
47   int i;
48 
49   mg_mgr_init(&mgr, NULL);
50 
51   /* Process command line arguments */
52   for (i = 1; i < argc; i++) {
53     if (strcmp(argv[i], s_show_headers_opt) == 0) {
54       s_show_headers = 1;
55     } else if (strcmp(argv[i], "--hexdump") == 0 && i + 1 < argc) {
56       mgr.hexdump_file = argv[++i];
57     } else {
58       break;
59     }
60   }
61 
62   if (i + 1 != argc) {
63     fprintf(stderr, "Usage: %s [%s] [--hexdump <file>] <URL>\n", argv[0],
64             s_show_headers_opt);
65     exit(EXIT_FAILURE);
66   }
67 
68   mg_connect_http(&mgr, ev_handler, argv[i], NULL, NULL);
69 
70   while (s_exit_flag == 0) {
71     mg_mgr_poll(&mgr, 1000);
72   }
73   mg_mgr_free(&mgr);
74 
75   return 0;
76 }
複製代碼

 

HTTP事件:
     正如在概述中所討論的,mg_set_protocol_http_websocket()函數解析傳入的數據,將其視爲HTTP或WebSocket,並觸發高級HTTP或WebSocket事件。下面是一個特定於HTTP的事件列表:
        MG_EV_HTTP_REQUEST:收到一個http請求,解析後的請求 整理爲struct http_message結構,並經過void * ev_data指針傳遞。
         MG_EV_HTTP_REPLY:收到一個http迴應,解析後的迴應整理爲struct http_message結構,並經過void * ev_data指針傳遞。
        
MG_EV_HTTP_MULTIPART_REQUEST:收到一個分多個包傳遞的http請求,在http的body開始解析前發送此事件。在此事件後用戶會收到一些列的MG_HTTP_PART_BEGIN/DATA/END請求。這也是最後一次訪問標頭和http中其餘請求字段。
        MG_EV_HTTP_CHUNK:收到一個http編碼塊,解析後的http應答整理爲struct http_message結構,並經過void * ev_data指針傳遞。http_message::body將包含從新組裝的不完整body塊。http_message::body伴隨着每一個新塊的到達而增加,此過程可能消耗大量內存。事件處理函數可在塊到來的時候處理body,並經過設置在mg_connection::flags設置MG_F_DELETE_CHUNK標記來告知mongoose去刪除已經解析處理過的body塊。(若是事件處理函數沒有告知mongoose去刪除已經處理過的body)當接收到最後一個0數據塊的時候,mongoose發送MG_F_HTTP_REPLY事件並帶有重組的完整的body.
    MG_F_HTTP_PART_BEGIN:開始接收 分包傳遞的消息的一部分消息,並在mg_http_multipart中國傳遞額外參數。
    MG_F_HTTP_PART_DATA:開始接收 分包傳遞的消息的新數據部分沒有額外的表頭可用只有 數據和數據大小
     MG_F_HTTP_PART_END:收到最後一個邊界,相似可能用來找到包的結尾。須要注意的是mongoose在編譯的時候應該設置MG_ENABLE_HTTP_MULTIPART去使能多部分事件。
Serving files:
    mg_serve_http()函數使得從文件系統提供文件變得時分簡單。通常來此函數是http server實現的用於提供cgi,ssl等靜態文件的。它的行爲合併到了struct mg_serve_http_opts結構的選項列表中.mg_serve_http()功能的完整列表可參考struct mg_serve_http_opts.
    爲了建立一個服務於當前目錄中的靜態文件的web服務器。按以下方式實現事件處理器函數:
複製代碼
 1 static void ev_handler(struct mg_connection *c, int ev, void *ev_data) {
 2   if (ev == MG_EV_HTTP_REQUEST) {
 3     struct mg_serve_http_opts opts;
 4 
 5     memset(&opts, 0, sizeof(opts);  // Reset all options to defaults
 6     opts.document_root = ".";       // Serve files from the current directory
 7 
 8     mg_serve_http(c, (struct http_message *) ev_data, s_http_server_opts);
 9   }
10 }
複製代碼
 
 
    有時候不須要完整的靜態web服務器。例如在RESTful的服務器上工做,若是某些端點必須返回靜態文件的內容,可以使用簡單的mg_http_serve_file()函數
複製代碼
 1 static void ev_handler(struct mg_connection *c, int ev, void *ev_data) {
 2    switch (ev) {
 3      case MG_EV_HTTP_REQUEST: {
 4        struct http_message *hm = (struct http_message *) ev_data;
 5        mg_http_serve_file(c, hm, "file.txt",
 6                           mg_mk_str("text/plain"), mg_mk_str(""));
 7        break;
 8      }
 9      ...
10    }
11   }
複製代碼
CGI:
    cgi是一種簡單的動態內容生成機制。爲了去使用cgi,調用mg_serve_http()函數並對cgi文件使用.cgi擴展名便可。更準確地說,匹配struct mg_serve_opts中cgi_file_pattern模式的文件都視爲cgi,若是cgi_file_pattern是NULL則**.cgi$或者**.php$均可使用。
    若是mongoose將一個文件看做是cgi文件,它會去執行此文件,並其輸出發送回client.所以cgi文件必須是可執行的。若是同時使用PHP和Perl CGIs,那麼在各自cgi腳本的第一行必須是#!/path/to/php-cgi.exe和 #!/path/to/perl.exe.能夠爲全部的cgi腳本硬編碼到cgi解釋器的路徑而不考慮shebang line。爲此在mg_serve_http_opts中設置cgi_interpreter.注意php腳本必須使用php-cgi.exe做爲cgi解析器而不是php.exe
opts.cgi_interpreter = "C:\\ruby\\ruby.exe";
 
      在cgi處理程序中,咱們沒有顯式地使用系統調用waitpid()來獲取殭屍進程。相反咱們將SIGCHLD處理程序設置爲SIG_IGN.其致使殭屍進程會自動獲取。入股全部的SIGCHLD被忽略,並非全部的操做系統都會獲取殭屍進程。
SSI:
文件上傳:
    爲了實現文件上傳,使用以下html
<form method="POST" action="/upload" enctype="multipart/form-data">
  <input type="file" name="file">
  <input type="submit" value="Upload">
</form>
 
    上傳文件會經過POST請求將文件發送到/upload目錄。HTTP的body將會包含文件內容的多部分編碼緩衝區。爲保存文件使用以下:
複製代碼
 1 struct mg_str cb(struct mg_connection *c, struct mg_str file_name) {
 2   // Return the same filename. Do not actually do this except in test!
 3   // fname is user-controlled and needs to be sanitized.
 4   return file_name;
 5 }
 6 
 7 void ev_handler(struct mg_connection *c, int ev, void *ev_data) {
 8   switch (ev) {
 9     ...
10     case MG_EV_HTTP_PART_BEGIN:
11     case MG_EV_HTTP_PART_DATA:
12     case MG_EV_HTTP_PART_END:
13       mg_file_upload_handler(c, ev, ev_data, cb);
14       break;
15   }
16 }
複製代碼
 
使能SSL(HTTPS):
    在http服務器端使能ssl須要按照以下步驟:
        1.獲取ssl證書和私鑰文件
        2.聲明struct mg_bind_opts,初始化化ssl_cert和ssl_key
        3.使用mg_bind_opt()去建立監聽socket
實例:(更多介紹:查看examples中的https server 實例)
複製代碼
 1 int main(void) {
 2   struct mg_mgr mgr;
 3   struct mg_connection *c;
 4   struct mg_bind_opts bind_opts;
 5 
 6   mg_mgr_init(&mgr, NULL);
 7 
 8   memset(&bind_opts, 0, sizeof(bind_opts));
 9   bind_opts.ssl_cert = "server.pem";
10   bind_opts.ssl_key = "key.pem";
11 
12   // Use bind_opts to specify SSL certificate & key file
13   c = mg_bind_opt(&mgr, "443", ev_handler, bind_opts);
14   mg_set_protocol_http_websocket(c);
15 
16   ...
17 }
複製代碼
 
    Digest身份認證
        mongoose有一個內置的digest(md5)認證支持。爲了啓用digest認證,須要再保護目錄中建立一個.htpasswd文件。此文件應該採用apache的htdigest使用程序的格式。
        你可使用Apache的htdigest實例,或者mongoose在   https://www.cesanta.com/binary.htm預構建一個二進制並添加新用戶到這個文件。
mongoose -A /path/to/.htdigest mydomain.com joe joes_password
 
通用API參考:
複製代碼
 1 struct http_message {
 2   struct mg_str message; /* Whole message: request line + headers + body */
 3   struct mg_str body;    /* Message body. 0-length for requests with no body */
 4 
 5   /* HTTP Request line (or HTTP response line) */
 6   struct mg_str method; /* "GET" */
 7   struct mg_str uri;    /* "/my_file.html" */
 8   struct mg_str proto;  /* "HTTP/1.1" -- for both request and response */
 9 
10   /* For responses, code and response status message are set */
11   int resp_code;
12   struct mg_str resp_status_msg;
13 
14   /*
15    * Query-string part of the URI. For example, for HTTP request
16    *    GET /foo/bar?param1=val1&param2=val2
17    *    |    uri    |     query_string     |
18    *
19    * Note that question mark character doesn't belong neither to the uri,
20    * nor to the query_string
21    */
22   struct mg_str query_string;
23 
24   /* Headers */
25   struct mg_str header_names[MG_MAX_HTTP_HEADERS];
26   struct mg_str header_values[MG_MAX_HTTP_HEADERS];
27 };///HTTP消息
28 struct websocket_message {
29   unsigned char *data;
30   size_t size;
31   unsigned char flags;
32 };///websocket消息
33 struct mg_http_multipart_part {
34   const char *file_name;
35   const char *var_name;
36   struct mg_str data;
37   int status; /* <0 on error */
38   void *user_data;
39 };///HTTP分包部分
40 struct mg_ssi_call_ctx {
41   struct http_message *req; /* The request being processed. */
42   struct mg_str file;       /* Filesystem path of the file being processed. */
43   struct mg_str arg; /* The argument passed to the tag: <!-- call arg -->. */
44 };///SSI調用上下文
45 void mg_set_protocol_http_websocket(struct mg_connection *nc);
46 void mg_send_websocket_handshake(struct mg_connection *nc, const char *uri,
47                                  const char *extra_headers);
48 void mg_send_websocket_handshake2(struct mg_connection *nc, const char *path,
49                                   const char *host, const char *protocol,
50                                   const char *extra_headers);
51 void mg_send_websocket_handshake3(struct mg_connection *nc, const char *path,
52                                   const char *host, const char *protocol,
53                                   const char *extra_headers, const char *user,
54                                   const char *pass);
55 void mg_send_websocket_handshake3v(struct mg_connection *nc,
56                                    const struct mg_str path,
57                                    const struct mg_str host,
58                                    const struct mg_str protocol,
59                                    const struct mg_str extra_headers,
60                                    const struct mg_str user,
61                                    const struct mg_str pass);
62 struct mg_connection *mg_connect_ws(struct mg_mgr *mgr,
63                                     MG_CB(mg_event_handler_t event_handler,
64                                           void *user_data),
65                                     const char *url, const char *protocol,
66                                     const char *extra_headers);
67 struct mg_connection *mg_connect_ws_opt(
68     struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
69     struct mg_connect_opts opts, const char *url, const char *protocol,
70     const char *extra_headers);
71 void mg_send_websocket_frame(struct mg_connection *nc, int op_and_flags,
72                              const void *data, size_t data_len);
73 void mg_send_websocket_framev(struct mg_connection *nc, int op_and_flags,
74                               const struct mg_str *strings, int num_strings);
75 void mg_printf_websocket_frame(struct mg_connection *nc, int op_and_flags,
76                                const char *fmt, ...);
77 int mg_url_decode(const char *src, int src_len, char *dst, int dst_len,
78                   int is_form_url_encoded);
79 extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[],
80                           const size_t *msg_lens, uint8_t *digest);
81 extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
82                            const size_t *msg_lens, uint8_t *digest);
83  
複製代碼

 

http server端API參考:
複製代碼
  1 int mg_parse_http(const char *s, int n, struct http_message *hm, int is_req);
  2 /**解析http消息,若是is_req=1 此消息是http請求,is_req=0 此消息是http迴應
  3  *返回解析的字節數 若是http消息不完整則返回0 若是解析出錯,則返回負數*/
  4 struct mg_str *mg_get_http_header(struct http_message *hm, const char *name);
  5 /**搜索並返回解析後的http消息hm中的表頭名稱,若是沒有找到標頭,則返回NULL
  6  *實例:struct mg_str *host_hdr = mg_get_http_header(hm, "Host");*/
  7 int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
  8                          size_t buf_size);
  9 /**解析http頭hdr,查找變量var_name並將其值存儲到buf中,若是沒有找到變量,返回0不然返回非0。此函數用於解析cookies驗證頭等。若是成功則返回變量值的長度,若是buf緩衝區不夠大,或者沒有找到此變量則返回0
 10 char user[20];
 11 struct mg_str *hdr = mg_get_http_header(hm, "Authorization");
 12 mg_http_parse_header(hdr, "username", user, sizeof(user));
 13 */
 14 int mg_get_http_basic_auth(struct http_message *hm, char *user, size_t user_len,
 15                            char *pass, size_t pass_len);
 16 /*獲取並解析受權,若是沒有找到受權頭或者mg_parse_http_basic_auth解析結果頭失敗*/
 17 int mg_parse_http_basic_auth(struct mg_str *hdr, char *user, size_t user_len,
 18                              char *pass, size_t pass_len);
 19 /*解析受權,如受權類型不是Basic或者出現其餘錯誤(如用於base64編碼的用戶密碼不對)基本頭返回-1*/
 20 size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name,
 21                           size_t var_name_len, char *file_name,
 22                           size_t file_name_len, const char **chunk,
 23                           size_t *chunk_len);
 24 /*解析包含多部分表單數據庫的緩衝區buf,buf_len,將塊名存儲在var_name,var_name_len緩衝區中。若是塊是上傳文件,那麼file_name,file_name_len將會被一個上傳的文件名填充。chunk,chunk_len指向塊數據。返回要跳到下一個塊的字節數,若是沒有更多塊則返回0*/
 25 int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst,
 26                     size_t dst_len);
 27 /*獲取Http表單變量。從buf獲取變量名,到指定dst指定長度dst_len的緩衝區。目的地址老是0終止,返回獲取到的變量的長度,若是沒有找到變量,則返回0.buf必須是有效的url編碼緩衝區。若是dst長度過小或者發生錯誤返回負數*/
 28 
 29 //此結構定義了mg_serve_http()的工做方式,最佳是設置須要的,其他爲NULL
 30 struct mg_serve_http_opts {
 31   const char *document_root;///web根目錄路徑
 32   const char *index_files;///索引文件列表,默認是""
 33   /*
 34   per_directory_auth_file =NULL表示禁用身份驗證
 35   per_directory_auth_file =".htpasswd" 要使用身份認證來保護目錄,而後在任何目錄建立使用                                 digest認證的.htpasswd文件。使用mongoose web服務器二進制文件或                             者Apache的htpasswd實例建立/操做密碼文件
 36                              確保auth_domain是一個有效的域
 37   */
 38   const char *per_directory_auth_file;
 39   const char *auth_domain;//受權域。即web服務器的域名
 40   /*
 41       global_auth_file = NULL 禁用身份認證
 42       一般只保護document_root根目錄選定的目錄。若是對web服務器的全部訪問都必須通過身份驗證,       無論URI是什麼,將此選項設置爲密碼文件的路徑。此文件的格式與.htpasswd的格式同樣,並把此        文件放在document_root根目錄以外,以防他人獲取到此文件
 43    */
 44   const char *global_auth_file;
 45   const char *enable_directory_listing;//設置爲"no"禁用目錄列表,默認啓用
 46   const char *ssi_pattern;///ssi匹配模式  源碼有詳細介紹
 47   const char *ip_acl;///ip_acl=NULL表示全部IP均可連接
 48 #if MG_ENABLE_HTTP_URL_REWRITES
 49       const char *url_rewrites;/////源碼有詳細介紹
 50 #endif
 51   const char *dav_document_root;///DAV的根目錄,dav_document_root=NULL 則DAV請求失敗
 52   const char *dav_auth_file;//DAV密碼文件,dav_auth_file=NULL 則DAV請求失敗 dav_auth_file="-" 禁用DAV鑑權
 53   const char *hidden_file_pattern;///文件隱藏的Glob模式
 54   const char *cgi_file_pattern;///cgi_file_pattern != NULL 則使能cgi,即此目錄下知足 **.cgi$|**.php$"都視爲cgi文件
 55   const char *cgi_interpreter;///若是不是NULL,請忽略CGI腳本hashbang並使用這個解釋器
 56   const char *custom_mime_types;
 57 /*Comma-separated list of Content-Type overrides for path suffixes,".txt=text/plain; charset=utf-8,.c=text/plain"e.g.*/
 58   const char *extra_headers;///添加到每一個http相應的額外的header,要啓用CORS,請將此設置爲"Access-Control-Allow-Origin: *"。
 59 }
 60 void mg_serve_http(struct mg_connection *nc, struct http_message *hm,
 61                    struct mg_serve_http_opts opts);
 62 /*根據opts的內容,提供特定的http請求*/
 63 void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
 64                         const char *path, const struct mg_str mime_type,
 65                         const struct mg_str extra_headers);
 66 /*提供具備給定MIME類型和可選extra_headers頭的特定文件*/
 67 typedef struct mg_str (*mg_fu_fname_fn)(struct mg_connection *nc,
 68                                         struct mg_str fname);///mg_file_upload_handler()的回調函數原型
 69 void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data,
 70                             mg_fu_fname_fn local_name_fn
 71                                 MG_UD_ARG(void *user_data));
 72 /*文件上傳處理程序。這個處理程序能夠用最少的代碼實現文件上傳.此程序處理MG_EV_HTTPPART事件並保存文件數據到本地。local_name_fn將以客戶端提供的名字調用並以指望的本地文件名稱打開。若是返回NULL將終止文件上傳(客戶端獲得403),若是返回不是NULL,返回的字符串必須是堆分配的,且調用方須要釋放這些字符串。異常狀況:返回徹底相同的字符串,
 73 */
 74 void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path,
 75                                MG_CB(mg_event_handler_t handler,
 76                                      void *user_data));
 77 /*爲nc註冊回調函數,若是註冊了回調函數,其會被調用歐冠,而不是調用mg_bind()中提供的回調函數*/
 78 struct mg_http_endpoint_opts {
 79   void *user_data;
 80   /*受權域 (realm) */
 81   const char *auth_domain;
 82   const char *auth_file;
 83 };
 84 void mg_register_http_endpoint_opt(struct mg_connection *nc,
 85                                    const char *uri_path,
 86                                    mg_event_handler_t handler,
 87                                    struct mg_http_endpoint_opts opts);
 88 int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
 89                               FILE *fp);
 90 /*根據打開的密碼文件驗證HTTP請求。若是通過身份驗證,返回1,不然返回0*/
 91 int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
 92                          struct mg_str username, struct mg_str cnonce,
 93                          struct mg_str response, struct mg_str qop,
 94                          struct mg_str nc, struct mg_str nonce,
 95                          struct mg_str auth_domain, FILE *fp);
 96 /*對打開的密碼文件驗證給定的響應參數。若是通過身份驗證,返回1,不然返回0。
 97 由 mg_http_check_digest_auth().調用
 98 */
 99 void mg_send_http_chunk(struct mg_connection *nc, const char *buf, size_t len);
100 /*
101 使用組塊HTTP編碼向客戶端發送大小爲len的緩衝區buf.這個函數首先將  發送緩衝區大小(16進制)+換行符+緩衝區+換行符發出。例如,mg_send_http_chunk(nc,「foo」,3)將把 "3\r\nfoo\r\n"字符串追加到nc->send_mbuf輸出IO緩衝區
102 HTTP頭「傳輸編碼:塊化」應該在使用此函數以前發送
103 不要忘記在響應結束時發送一個空塊,告訴客戶端全部內容都已發送
104 */
105 void mg_printf_http_chunk(struct mg_connection *nc, const char *fmt, ...);
106 /*發送一個printf格式的HTTP塊。功能相似於mg_send_http_chunk()。*/
107 void mg_send_response_line(struct mg_connection *nc, int status_code,
108                            const char *extra_headers);
109 /*發送響應狀態行。若是extra_headers不是NULL,那麼extra_headers也會在響應行以後發送。extra_headers不能以新行結束*/
110 void mg_http_send_error(struct mg_connection *nc, int code, const char *reason);
111 /*發送一個錯誤的迴應。若是緣由爲空,消息將從錯誤代碼中推斷出來(若是支持的話)*/
112 void mg_http_send_redirect(struct mg_connection *nc, int status_code,
113                            const struct mg_str location,
114                            const struct mg_str extra_headers);
115 /*發送一個重定向響應。status_code應該是301或302,位置指向新位置。若是extra_headers不是空的,那麼extra_headers也會在響應行以後發送。extra_headers不能以新行結束。*/
116 void mg_send_head(struct mg_connection *n, int status_code,
117                   int64_t content_length, const char *extra_headers);
118 /*發送響應行和標題。這個函數用status_code發送響應行,並自動發送一個標題:"Content-Length"或 "Transfer-Encoding"。若是content_length爲負,則發送「Transfer-Encoding: chunked」報頭,不然發送「Content-Length」報頭。若是轉換編碼被分割,那麼消息體必須使用mg_send_http_chunk()或mg_printf_http_chunk()函數發送。不然,必須使用mg_send()或mg_printf()。額外的標題能夠經過extra_headers設置。注意,extra_headers不能被一個新的行終止*/
119 void mg_printf_html_escape(struct mg_connection *nc, const char *fmt, ...);
120 /*發送一個打印格式的HTTP塊,轉義HTML標記*/
121 void mg_http_reverse_proxy(struct mg_connection *nc,
122                            const struct http_message *hm, struct mg_str mount,
123                            struct mg_str upstream);
124 /*將給定的請求代理給給定的上游http服務器.掛載中的路徑前綴將被剝離到上游服務器請求的路徑,例如若是掛載爲/api,上游爲 http://localhost:8001/foo 那麼向/api傳入的請求將傳入到 http://localhost:8001/foo/bar*/
125  
126  
複製代碼

 

client 端API參考:
複製代碼
 1 struct mg_connection *mg_connect_http(
 2     struct mg_mgr *mgr,
 3     MG_CB(mg_event_handler_t event_handler, void *user_data), const char *url,
 4     const char *extra_headers, const char *post_data);
 5 /*建立出站HTTP鏈接的助手函數
 6 url是要獲取的url。它必須被正確地url編碼,例如沒有空格,等等.默認狀況下,mg_connect_http()發送鏈接和主機頭
 7 extra_headers是一個額外的HTTP頭:"User-Agent: my-app\r\n"
 8 若是post_data爲空,則建立一個GET請求。不然,將使用指定的POST數據建立POST請求。
 9 注意,若是要提交的數據是表單提交,那麼應該相應地設置Content-Type報頭(參見下面的示例)。
10 nc1 = mg_connect_http(mgr, ev_handler_1, "http://www.google.com&quot;, NULL,
11                         NULL);
12 nc2 = mg_connect_http(mgr, ev_handler_1, "https://github.com&quot;, NULL, NULL);
13 nc3 = mg_connect_http(
14       mgr, ev_handler_1, "my_server:8000/form_submit/",
15       "Content-Type: application/x-www-form-urlencoded\r\n",
16       "var_1=value_1&var_2=value_2");
17 */
18 struct mg_connection *mg_connect_http_opt(
19     struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
20     struct mg_connect_opts opts, const char *url, const char *extra_headers,
21     const char *post_data);
22 /*建立出站HTTP鏈接的助手函數.與mg_connect_http基本相同,但容許提供額外的參數(例如SSL參數)*/
23 int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
24                                       const char *method, const char *uri,
25                                       const char *auth_domain, const char *user,
26                                       const char *passwd);
27 /*爲客戶機請求建立摘要身份驗證頭*/
複製代碼

 

0
0
相關文章
相關標籤/搜索