티스토리 뷰

요즘은 대부분 웹 인터페이스를 제공한다. 심지어 기기, 가전에 이르기까지 웹인터페이스를 제공하는게 추세다. 보통 이러한제품들에 탑재하기 위해서는 일단 가볍고 볼 일이다. 포팅이 용이하고, 쓸데없이 많은 기능을 가질 필요는 없는 것이다. 반드시 필요한 요소만 간추려서 제공하고 포팅이 용이한 가변운 웹서버가 제격이다.

 가벼운 웹서버들의 종류는 (링크)  많다.

위 링크에서 언급된 웹서버들 중에서 오늘은 Mongoose를 리뷰해 보고자 한다.

C언어로 개발되었고, 지원하는 플랫폼은 Windows, MacOS, Unix, QNX, Android, iOS등이다. 라이센스는 MIT라이센스이다. 현재 버전은 3.7이고, 글 쓰는 시점(2013-07-10)에서 최신 버전은 2013-02-05에 릴리즈되었다.

아래는 간략한 오버뷰이다.

  • Liberal, commercial-friendly MIT license
  • Works on Windows, Mac, UNIX, iPhone, Android, and many other platforms
  • Support for CGI, SSL, SSI, Digest (MD5) authorization, Websocket, WEbDAV
  • Lua server pages (PHP-like functionality using Lua) with SQLite3, see page.lp
  • Resumed download, URL rewrite, IP-based ACL, Windows service
  • Excluding files from serving by URI pattern (file blacklist)
  • Download speed limit based on client subnet or URI pattern
  • Small footprint: Mongoose executable size is 64 kB on Linux 2.6 i386 system
  • 320 kilobytes Windows executable with all of the above (including SSL, Lua, Sqlite) and no dependencies
  • Simple and clean embedding API, mongoose.h. The source is in single mongoose.c file to make things easy.
  • HTTP client functionality for embedded usage, capable of sending arbitrary HTTP/HTTPS requests
  • Embedding examples: hello.cpost.cupload.cwebsocket.c
  • User Manual

간단 사용을 해보니, 파일하나(mongoose.c mongoose.h)로 이뤄진 점이 신선하다. 이 파일에서 제공하는 API를 사용하여 간단하게 임베딩이 가능한 것이다.

내부구조

당연하게도, 임베딩하기 위해서는 제어구조(flow of control)을 알아야 한다. 그래야 임베딩 하는 전략을 세울 수 있기 때문이다. 임베딩을 위한 프로그램 플로우에 대한 고려에 관한한 아주 쉽게 되어 있는데, mongoose가 멀티쓰레드 웹서버이기 때문이다.

mg_start() 함수는  웹 서버 컨텍스트( struct mg_context)를 할당하는데,  이 자료구조는 웹 서버 인스턴스에 대한 모든 정보를 쥐고 있는 핵심 자료구조이다. 구체적으로는 다음과 같다.

  • configuration options. Note that mongoose makes internal copies of passed options.
  • SSL context, if any
  • user-defined callbacks
  • opened listening sockets
  • a queue for accepted sockets
  • mutexes and condition variables for inter-thread synchronization
mg_start() 함수에서 리턴되면, 모든 초기화가 완전하게 되었음을 보장한다. (예를 들어, listening port가 열림, SSL이 초기화됨 등등). mg_start()는 2개의 쓰레드를 시작하는데, 하나는 http연결을 받아 들이는(accept)하는 쓰레드이고, 몇몇은 작업 쓰레드들인데, 연결에 대한 프로세싱을 하는 것들이다. 작업 쓰레드의 수는 num_threads라는 환경 옵션에 의해 구성가능하다. 이 숫자가 지니는 의미는 mongoose에 의해 핸들링되는 동시 요청의 수에 대한 제한으로 둔다.
 

When master thread accepts new connection, a new accepted socket (described by struct socket) it placed into the accepted sockets queue, which has size of 20 (see code). Any idle worker thread can grab accepted sockets from that queue. If all worker threads are busy, master thread can accept and queue up to 20 more TCP connections, filling up the queue. In the attempt to queue next accepted connection, master thread blocks until there is space in a queue. When master thread is blocked on a full queue, TCP layer in OS can also queue incoming connection. The number is limited by the listen() call parameter on listening socket, which is SOMAXCONN in case of Mongoose, and depends on a platform.

Worker threads are running in an infinite loop, which in simplified form looks something like this:

static void *worker_thread() {
  while (consume_socket()) {
    process_new_connection();
  }
}

Function consume_socket() gets new accepted socket from the mongoose socket queue, atomically removing it from the queue. If the queue is empty, consume_socket() blocks and waits until new sockets are placed in a queue by the master thread.process_new_connection() actually processes the connection, i.e. reads the request, parses it, and performs appropriate action depending on a parsed request.

Master thread uses poll() and accept() to accept new connections on listening sockets. poll() is used to avoidFD_SETSIZE limitation of select(). Since there are only a few listening sockets, there is no reason to use hi-performance alternatives like epoll() or kqueue(). Worker threads use blocking IO on accepted sockets for reading and writing data. All accepted sockets have SO_RCVTIMEO and SO_SNDTIMEO socket options set (controlled by request_timeout_ms mongoose option, 30 seconds default) which specify read/write timeout on client connection.

Other Resources

  • Presentation made by Arnout Vandecappelle at FOSDEM 2011 on 2011-02-06 in Brussels, Belgium, called "Creating secure web based user interfaces for Embedded Devices" (pdf | odp)
  • Linux Journal article by Michel J.Hammel, 2010-04-01, called Mongoose: an Embeddable Web Server in C