Skip to content

brilliantshell/webserv

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

BrilliantServer

λͺ©μ°¨


0 Introduction

  • BrilliantServer λŠ” RFC 9110, 9112 에 μ •μ˜λœ κ·œκ²©μ„ λ”°λ₯΄λŠ” HTTP/1.1 버전 origin server 의 κ΅¬ν˜„μž…λ‹ˆλ‹€.
  • 이 ν”„λ‘œμ νŠΈλŠ” Google C++ Style Guide λ₯Ό 따라 C++98 둜 μž‘μ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

0.0 κΈ°λŠ₯ μ†Œκ°œ

  • kqueue λ₯Ό ν™œμš©ν•œ event loop 기반 non-blocking I/O multiplexing
  • RFC 3875 λ₯Ό λ”°λ₯΄λŠ” CGI 지원
  • static νŒŒμΌμ— λŒ€ν•œ GET/POST/DELETE HTTP Request 처리
    • POST λ©”μ†Œλ“œλ‘œ 파일 μ—…λ‘œλ“œ
  • HTTP κ·œκ²©μ— λ§žλŠ” HTTP 응닡 생성
  • name-based virtual hosting 지원
  • directory listing 지원

0.1 μ„€μΉ˜ 및 μ‹€ν–‰

git clone https://github.com/brilliantshell/webserv.git && \
cd webserv && \
make -j
./BrilliantShell [path/to/config]

config 파일 μž‘μ„± κ·œμΉ™μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€. Configuration 파일 κ·œμΉ™

0.2 섀계

섀계 figma νŽ˜μ΄μ§€

0.2.0 객체 관계도

BrilliantServer α„€α…’α†¨α„Žα…¦ α„€α…ͺᆫ계도.png

0.2.1 객체 μ±…μž„/μ—­ν• 

responsibilities.png

0.2.2 HTTP μš”μ²­/응닡 flowchart

http_flowchart.png


1 socket 톡신

1.0 socket μ΄λž€

  • socket 톡신은 IPC (Inter Process Communication) 의 ν•œ μ’…λ₯˜λ‹€.

  • socket νŒŒμΌμ€ socket ν†΅μ‹ μ˜ μ’…μ μœΌλ‘œ (endpoint) socket νŒŒμΌμ„ μ—° ν”„λ‘œκ·Έλž¨μ€ socket νŒŒμΌμ„ μ—° λ‹€λ₯Έ ν”„λ‘œκ·Έλž¨κ³Ό connection 을 μˆ˜λ¦½ν•˜κ±°λ‚˜, μ„œλ‘œμ˜ μ£Όμ†Œλ‘œ datagram 을 μ „μ†‘ν•˜μ—¬ μ„œλ‘œ 톡신할 수 μžˆλ‹€.

  • socket ν•¨μˆ˜λ‘œ socket νŒŒμΌμ„ μ—΄ 수 있으며, 성곡 μ‹œ ν• λ‹Ή 된 fd κ°€ λ°˜ν™˜λœλ‹€.

    #include <sys/socket.h>
    int socket(int domain, int type, int protocol);
    • domain 둜 μ–΄λ–€ λ„λ©”μΈμ—μ„œ 톡신할지 μ •ν•  수 μžˆλ‹€. μ—¬λŸ¬κ°œκ°€ μžˆμ§€λ§Œ (OS X μ—λŠ” 8개, Linux μ—λŠ” 더 λ§Žλ‹€) μ€‘μš”ν•œ λ‘κ°œλŠ” PF_LOCAL κ³Ό PF_INET 이닀.
      • PF_LOCAL - 도메인/UNIX socket 이라고 λΆ€λ₯΄λŠ” 둜컬 ν”„λ‘œμ„ΈμŠ€ κ°„ 톡신을 μœ„ν•œ 도메인
      • PF_INET - TCP socket 이라고 λΆ€λ₯΄λŠ” IP 톡신을 μœ„ν•œ 도메인
    • type 둜 μ „μ†‘ν•˜λŠ” λ°μ΄ν„°μ˜ λ‹¨μœ„λ₯Ό μ •ν•  수 μžˆλ‹€.
      • SOCK_STREAM - connection 수립 & byte stream 으둜 톡신
      • SOCK_DGRAM - μƒλŒ€μ˜ μ£Όμ†Œλ‘œ datagram 으둜 톡신
      • SOCK_RAW - datagram 으둜 톡신, λ‚΄λΆ€ λ„€νŠΈμ›Œν¬ μΈν„°νŽ˜μ΄μŠ€μ— μ ‘κ·Ό κ°€λŠ₯
    • protocol μ—λŠ” μ†ŒμΌ“μ΄ λ”°λ₯Ό ν”„λ‘œν† μ½œμ„ μ§€μ •ν•œλ‹€. 같은 ν”„λ‘œν† μ½œλ‘œ μ—΄λ¦° μ†ŒμΌ“λ“€λΌλ¦¬λ§Œ 톡신이 κ°€λŠ₯ν•˜λ‹€. TCP λŠ” 6. (/etc/protocols μ°Έκ³ )

    πŸ’‘ 이 λ¬Έμ„œλŠ” Web Server 에 κ΄€λ ¨λœ λ¬Έμ„œμ΄κΈ° λ•Œλ¬Έμ— μ•„λž˜μ—μ„œλŠ” TCP socket 에 λŒ€ν•΄μ„œλ§Œ μ„€λͺ…ν•œλ‹€.

1.1 μ£Όμ†Œ ν• λ‹Ή

  • socket 생성 ν›„ bind ν•¨μˆ˜λ‘œ ν•΄λ‹Ή μ†ŒμΌ“μ— μ£Όμ†Œ/μ‹λ³„μžλ₯Ό λΆ€μ—¬ν•  수 μžˆλ‹€.

    #include <sys/socket.h>
    int bind(int socket, const struct sockaddr *address, socklen_t address_len);
    • socket 은 ν•΄λ‹Ή socket 의 fd 이닀.
    • address λŠ” ν• λ‹Ήν•  μ£Όμ†Œ 정보λ₯Ό λ‹΄λŠ” ꡬ쑰체의 μ£Όμ†Œκ°’μ΄λ‹€. UNIX socket 의 경우 struct sockaddr_un 의 μ£Όμ†Œκ°’μ„, TCP socket 의 경우 struct sockaddr_in (IPv4) / struct sockaddr_in6 (IPv6) 의 μ£Όμ†Œκ°’μ„ μΊμŠ€νŒ…ν•΄μ„œ λ„£μ–΄μ€€λ‹€.
    • address_len μ—λŠ” address ꡬ쑰체의 길이λ₯Ό λ„£μ–΄μ€€λ‹€.
  • IPv4 의 경우 μ£Όμ†Œ κ΅¬μ‘°μ²΄λŠ” μ•„λž˜μ™€ κ°™λ‹€.

    struct in_addr {
    	in_addr_t s_addr;
    };
    
    struct sockaddr_in {
    	__uint8_t       sin_len;
    	sa_family_t     sin_family; // AF_INET
    	in_port_t       sin_port; // port number
    	struct  in_addr sin_addr; // listen ν•  IP μ£Όμ†Œ sin_addr.s_addr 에 μ„€μ •
    	char            sin_zero[8];
    };
    • sin_addr.s_addr λŠ” Server 의 경우 INADDR_ANY(0) 둜 μ„€μ •ν•˜μ—¬ μ–΄λ–€ μ£Όμ†Œλ“ μ§€ sin_port 에 μ„€μ •ν•œ port 둜 연결을 μ‹œλ„ν•˜λ©΄ listen ν•˜κ²Œ μ„€μ •ν•œλ‹€.
    • port 와 address λŠ” network byte order 둜 μ €μž₯λΌμ•Όν•˜κΈ° λ•Œλ¬Έμ— <arpa/inet.h> ν•¨μˆ˜λ“€μ„ ν™œμš©ν•΄μ•Όν•œλ‹€.

1.2 TCP μ—°κ²° 수립 & Passive vs. Active

  • HTTP Server λŠ” socket νŒŒμΌμ„ μ—΄μ–΄ (PF_INET, SOCK_STREAM, 6) Client ν”„λ‘œκ·Έλž¨λ“€κ³Ό TCP connection 을 μˆ˜λ¦½ν•˜μ—¬ ν†΅μ‹ ν•œλ‹€. (HTTP/3.0 λΆ€ν„°λŠ” UDP μ‚¬μš©)

  • Server 와 연결을 μ‹œλ„ν•˜λŠ” Client 의 socket 은 β€œactive” socket, Client 의 μ—°κ²° μ‹œλ„λ₯Ό κΈ°λ‹€λ¦¬λŠ” Server 의 socket 은 β€œpassive” socket 이라고 λΆ€λ₯Έλ‹€.

  • socket κ°„μ˜ TCP connection 이 μˆ˜λ¦½λ˜λŠ” 과정을 순차적으둜 μ„€λͺ…ν•˜λ©΄ μ•„λž˜μ™€ κ°™λ‹€.

    • bind 이후 Server 의 μ†ŒμΌ“μ€ listen ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜μ—¬ β€œpassive”/”listening” μƒνƒœλ‘œ μ „ν™˜ν•œλ‹€.

      #include <sys/socket.h>
      int listen(int socket, int backlog);
      • socket 은 ν•΄λ‹Ή socket 의 fd 이닀.
      • backlog λŠ” μ—°κ²° μˆ˜λ¦½μ„ κΈ°λ‹€λ¦¬λŠ” μš”μ²­λ“€μ˜ queue 의 μ΅œλŒ€ 길이이닀. queue κ°€ 꽉 μ°¨μžˆλŠ” μƒνƒœμ—μ„œ μ—°κ²° 수립 μš”μ²­μ΄ 였면 Client λŠ” ECONNREFUSED μ—λŸ¬λ₯Ό λ°˜ν™˜ λ°›λŠ”λ‹€. (silent limit 128)
    • Server λŠ” β€œlistening” socket 이 μ€€λΉ„λœ ν›„ accept ν•¨μˆ˜λ‘œ block ν•˜λ©° Client 의 μ—°κ²° 수립 μš”μ²­μ„ κΈ°λ‹€λ¦°λ‹€.

      #include <sys/socket.h>
      
      int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len);
      • socket 은 β€œpassive” socket 의 fd 이닀.
      • address λŠ” Client 의 β€œactive” socket 의 μ£Όμ†Œ 정보λ₯Ό 담을 ꡬ쑰체의 μ£Όμ†Œλ‹€.
      • address_len μ—λŠ” address ꡬ쑰체의 길이λ₯Ό λ„£μ–΄μ€€λ‹€.
    • Client μ†ŒμΌ“μ€ connect ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜μ—¬ Server 에 TCP μ—°κ²° μˆ˜λ¦½μ„ μš”μ²­ν•˜λŠ” β€œactive” 역할을 μˆ˜ν–‰ν•œλ‹€.

      #include <sys/types.h>
      #include <sys/socket.h>
      
      int connect(int socket, const struct sockaddr *address, socklen_t address_len);
      • socket 은 ν•΄λ‹Ή socket 의 fd 이닀.
      • address μ—λŠ” μ—°κ²°ν•˜κ³ μž ν•˜λŠ” Server 의 μ†ŒμΌ“μ— ν• λ‹Ήλœ μ£Όμ†Œκ°€ μž…λ ₯된 ꡬ쑰체의 μ£Όμ†Œκ°’μ„ λ„£μ–΄μ€€λ‹€.
      • address_len μ—λŠ” address ꡬ쑰체의 길이λ₯Ό λ„£μ–΄μ€€λ‹€.
    • Client 의 μ—°κ²° 수립 μš”μ²­μ„ μˆ˜μ‹ ν•˜λ©΄ accept λŠ” blocking 을 ν’€κ³  Client 의 β€œactive” socket κ³Ό 연결이 μˆ˜λ¦½ν•  μƒˆλ‘œμš΄ AF_INET socket 을 μƒμ„±ν•˜κ³ , 연결이 수립되면 (TCP ESTABLISHED) κ·Έ socket 의 fd λ₯Ό λ°˜ν™˜ν•œλ‹€.

1.3 socket I/O

  • Server λŠ” accept ν•¨μˆ˜κ°€ λ°˜ν™˜ν•œ fd 에 read/recv ν•˜μ—¬ Client κ°€ 보낸 μš”μ²­μ„ 읽고, write/send ν•˜μ—¬ Client μ—κ²Œ 응닡을 보낼 수 μžˆλ‹€.

    > πŸ’‘ socket 에 read/write ν•  λ•Œ, ν•œλ²ˆμ˜ 호좜둜 μ‹œμŠ€ν…œμ˜ TCP window size λ₯Ό λ„˜μ„ 수 μ—†λ‹€.
    

    sysctl -a | grep buf 둜 max limit 을 확인할 수 μžˆλ‹€. (auto * bufmax κ°€ window size)

    ![tcp max buffer](/assets/tcp-max-buffer.png)
    

1.4 socket μ„€μ •

  • socket 섀정을 getsockopt 둜 확인, setsockopt 둜 λ³€κ²½ν•  수 μžˆλ‹€.

    #include <sys/socket.h>
    
    int getsockopt(int socket, int level, int option_name, void *restrict option_value,
    socklen_t *restrict option_len);
    
    int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len);
  • setsockopt 의 option_name 인자둜 send/recv buffer size (SO_SNDBUF/SO_RCVBUF) λ“± μ—¬λŸ¬κ°€μ§€ 속성을 λ³€κ²½ν•  수 μžˆλ‹€.

1.4.0 SO_REUSEADDR vs SO_LINGER

  • νŠΉμ • ip + port 둜 bind λ˜μ–΄μžˆλŠ” passive socket 이 Server μ’…λ£Œ μ‹œ ν˜Ήμ€ μ‹€ν–‰ 쀑 μ–΄λ–€ 이유둜 λ‹«ν˜”μ„ λ•Œ ν•΄λ‹Ή socket 은 TIME-WAIT μƒνƒœκ°€ 되고 νŠΉλ³„ν•œ 섀정이 μ—†μ—ˆλ‹€λ©΄ 2MSL λ™μ•ˆ ν•΄λ‹Ή ip + port 둜의 bind κ°€ λΆˆκ°€λŠ₯해진닀.

  • Server κ°€ μž¬μ‹€ν–‰ν•˜κΈ° μœ„ν•΄ μ’…λ£Œ ν›„ 2MSL 을 κΈ°λ‹€λ €μ•Όν•˜λŠ” 것은 λ„ˆλ¬΄ λΆˆνŽΈν•˜κΈ° λ•Œλ¬Έμ—, 이λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ BrilliantServer 의 passive socket 듀은 SO_REUSEADDR 둜 μ„€μ •λ˜μ—ˆλ‹€. νŠΉμ • ip + port 둜 bind ν•˜κΈ° 전에 SO_REUSEADDR 섀정을 ν•˜λ©΄, Server λŠ” 2MSL 을 기닀리지 μ•Šκ³  λ°”λ‘œ ν•΄λ‹Ή ip + port λ₯Ό μž¬μ‚¬μš© (λ‹€μ‹œ bind ) ν•  수 μžˆλ‹€. TIME-WAIT socket 듀이 λ‚¨μ§€λ§Œ μ΄λŠ” 정상적인 μ’…λ£Œ 절차이고, Server μ—κ²Œ λ¬Έμ œκ°€ λ˜μ§€ μ•ŠλŠ”λ‹€.

    // opt μ—λŠ” 0 이 μ•„λ‹Œ μˆ«μžκ°€ λ“€μ–΄κ°€λ©΄ λœλ‹€ (bool 같은 μ—­ν• )
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
      PRINT_ERROR("address cannot be reused : " << strerror(errno));
      close(fd);
      return -1;
    }
    errno = 0;
    if (bind(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
      PRINT_ERROR("socket for " << kPort
                                << " cannot be bound : " << strerror(errno));
      close(fd);
      return -1;
    }
    listen(fd, BACKLOG);
  • SO_LINGER μ˜΅μ…˜μœΌλ‘œλ„ 이 문제λ₯Ό ν•΄κ²°ν•  수 μžˆλ‹€. SO_LINGER μ˜΅μ…˜μ€ struct linger 의 μ£Όμ†Œλ₯Ό setsockopt 의 option_value 둜 λ„˜κ²¨μ£Όλ©°, l_onoff κ°€ 0 이 μ•„λ‹ˆκ³  l_linger κ°€ μ–‘μ˜ μ •μˆ˜λ‘œ 섀정될 경우, Server κ°€ socket 을 close ν–ˆμ„ λ•Œ 아직 보내지지 μ•Šμ€ 데이터가 남아 μžˆλ‹€λ©΄ l_linger 초 만큼 close λ₯Ό block ν•˜κ²Œ μ„€μ •ν•˜λŠ”λ° μ‚¬μš©λœλ‹€. l_linger 값을 0 으둜 μ„€μ •ν•˜λ©΄ 정상적인 TCP μ—°κ²° μ’…λ£Œ μ ˆμ°¨κ°€ μ‹œμž‘λ˜μ§€ μ•Šκ³ , TCP μ—°κ²°μ—μ„œ RST control bit 이 보내지며 close ν•œ socket 이 TIME-WAIT μƒνƒœμ— 빠지지 μ•ŠλŠ”λ‹€. ν•˜μ§€λ§Œ λΉ„μ •μƒμ μœΌλ‘œ TCP 연결을 끊기 λ•Œλ¬Έμ— 이전 TCP 연결이 μ œλŒ€λ‘œ μ •λ¦¬λ˜μ§€ μ•Šμ•„ Connection Reset by Peer μ—λŸ¬κ°€ λ°œμƒν•  μœ„ν—˜μ΄ 크닀.

    struct  linger {
    	int     l_onoff;                /* option on (0)/off (non-zero) */
    	int     l_linger;               /* linger time (sec) */
    };

2 TCP Connection

2.0 TCP μƒνƒœ 전이도와 헀더 ꡬ쑰

  • TCP connection state diagram

                                +---------+ ---------\      active OPEN
                                |  CLOSED |            \    -----------
                                +---------+<---------\   \   create TCB
                                  |     ^              \   \  snd SYN
                     passive OPEN |     |   CLOSE        \   \
                     ------------ |     | ----------       \   \
                      create TCB  |     | delete TCB         \   \
                                  V     |                      \   \
              rcv RST (note 1)  +---------+            CLOSE    |    \
           -------------------->|  LISTEN |          ---------- |     |
          /                     +---------+          delete TCB |     |
         /           rcv SYN      |     |     SEND              |     |
        /           -----------   |     |    -------            |     V
    +--------+      snd SYN,ACK  /       \   snd SYN          +--------+
    |        |<-----------------           ------------------>|        |
    |  SYN   |                    rcv SYN                     |  SYN   |
    |  RCVD  |<-----------------------------------------------|  SENT  |
    |        |                  snd SYN,ACK                   |        |
    |        |------------------           -------------------|        |
    +--------+   rcv ACK of SYN  \       /  rcv SYN,ACK       +--------+
       |         --------------   |     |   -----------
       |                x         |     |     snd ACK
       |                          V     V
       |  CLOSE                 +---------+
       | -------                |  ESTAB  |
       | snd FIN                +---------+
       |                 CLOSE    |     |    rcv FIN
       V                -------   |     |    -------
    +---------+         snd FIN  /       \   snd ACK         +---------+
    |  FIN    |<----------------          ------------------>|  CLOSE  |
    | WAIT-1  |------------------                            |   WAIT  |
    +---------+          rcv FIN  \                          +---------+
      | rcv ACK of FIN   -------   |                          CLOSE  |
      | --------------   snd ACK   |                         ------- |
      V        x                   V                         snd FIN V
    +---------+               +---------+                    +---------+
    |FINWAIT-2|               | CLOSING |                    | LAST-ACK|
    +---------+               +---------+                    +---------+
      |              rcv ACK of FIN |                 rcv ACK of FIN |
      |  rcv FIN     -------------- |    Timeout=2MSL -------------- |
      |  -------            x       V    ------------        x       V
       \ snd ACK              +---------+delete TCB          +---------+
         -------------------->|TIME-WAIT|------------------->| CLOSED  |
                              +---------+                    +---------+
    
  • TCP Header Format

        0                   1                   2                   3
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |          Source Port          |       Destination Port        |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                        Sequence Number                        |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                    Acknowledgment Number                      |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       | Offset| Rsrvd |W|C|R|C|S|S|Y|I|            Window             |
       |       |       |R|E|G|K|H|T|N|N|                               |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |           Checksum            |         Urgent Pointer        |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                           [Options]                           |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                                                               :
       :                             Data                              :
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
              Note that one tick mark represents one bit position.
    
    • Seqeunce Number : 32 bits
      • ν•΄λ‹Ή μ„Έκ·Έλ¨ΌνŠΈμ˜ 첫 데이터 octet 의 sequence 번호. μ˜ˆμ™Έλ‘œ SYN 컨트둀 λΉ„νŠΈκ°€ μ„ΈνŒ…λ  λ•ŒλŠ” sequence λ²ˆν˜ΈλŠ” ISN, 첫 데이터 octet 은 ISN + 1 둜 μ„€μ •λœλ‹€.
    • Acknowledgement Number : 32 bits
      • ACK 컨트둀 λΉ„νŠΈκ°€ μ„ΈνŒ…λ˜λ©΄, λ‹€μŒμ— 받을 κ²ƒμœΌλ‘œ μ˜ˆμƒλ˜λŠ” μ„Έκ·Έλ¨ΌνŠΈμ˜ μ‹œν€€μŠ€ λ²ˆν˜Έκ°€ μ„€μ •λœλ‹€. ν•œ 번 연결이 수립되면 항상 μ „μ†‘λœλ‹€.
    • Reserved (Rsrvd) : 4 bits
      • 컨트둀 λΉ„νŠΈλ₯Ό ν‘œμ‹œν•œλ‹€.
    • Window : 16 bits
      • λ°œμ‹ μžκ°€ 받을 수 μžˆλŠ” TCP Window 크기 (unsigned number)

2.1 Establishing a Connection

  • three-way-handshake

        TCP Peer A                                           TCP Peer B
    
    1.  CLOSED                                               LISTEN
    
    2.  SYN-SENT    --> <SEQ=100><CTL=SYN>               --> SYN-RECEIVED
    
    3.  ESTABLISHED <-- <SEQ=300><ACK=101><CTL=SYN,ACK>  <-- SYN-RECEIVED
    
    4.  ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK>       --> ESTABLISHED
    
    5.  ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK><DATA> --> ESTABLISHED
    
    • μƒˆ 컀λ„₯μ…˜μ„ λ§Œλ“€κΈ° μœ„ν•΄μ„œλŠ” 각 Peer λ³„λ‘œ 32bit 크기의 ISN(Initial Sequence Number) 을 μƒμ„±ν•˜κ³  μ‹λ³„μžλ‘œμ¨ μ‚¬μš©ν•œλ‹€. ISN 을 μ‚¬μš©ν•˜λ©΄ 포트λ₯Ό μž¬μ‚¬μš© ν•  경우 각 컀λ„₯μ…˜μ„ κ΅¬λΆ„ν•˜μ—¬ 데이터가 ν˜Όμž¬λ˜μ§€ μ•Šκ³  SEQ λ₯Ό μΆ”μΈ‘ν•˜κΈ° μ–΄λ €μ›Œμ§€λ―€λ‘œ λ³΄μ•ˆμ΄ κ°•ν™”λœλ‹€.
    • 데이터λ₯Ό μ£Όκ³  받을 수 μžˆλŠ” μƒνƒœκ°€ 되렀면 두 peer λͺ¨λ‘ ESTABLISHED μƒνƒœκ°€ λ˜μ–΄μ•Ό ν•œλ‹€
    1. μ„œλ²„ (μœ„ κ·Έλ¦Όμ—μ„œ Peer B) μΈ‘μ—μ„œ passive open 으둜 LISTEN μƒνƒœκ°€ 되면 ν΄λΌμ΄μ–ΈνŠΈ (Peer A) κ°€ active open 을 μ‹œλ„ν•˜κΈΈ κΈ°λ‹€λ¦°λ‹€.
    2. ν΄λΌμ΄μ–ΈνŠΈκ°€ passive open ν•˜λ©΄ μžμ‹ μ˜ ISN 을 μ‹œν€€μŠ€ λ„˜λ²„ (SEQ) 둜 SYN 컨트둀 λΉ„νŠΈμ™€ ν•¨κ»˜ μ„œλ²„μ—κ²Œ 보내고 SYN_SENT 둜 μ „ν™˜ν•œλ‹€. SYN 을 λ°›μ•˜μœΌλ―€λ‘œ μ„œλ²„λŠ” SYN_RECEIVED μƒνƒœλ‘œ μ „ν™˜ν•œλ‹€.
    3. μ„Έκ·Έλ¨ΌνŠΈλ₯Ό λ°›μœΌλ©΄ μ„œλ²„λŠ” μžμ‹ μ˜ ISN을 μ‹œν€€μŠ€ λ„˜λ²„(SEQ) 둜, λ°›μ•˜λ˜ μ„Έκ·Έλ¨ΌνŠΈμ˜ SEQ + 1 값을 ACK 으둜 μ„€μ •ν•˜μ—¬ 보낸닀. 컨트둀 λΉ„νŠΈ SYN, ACK 둜 확인 응닡을 받은 ν΄λΌμ΄μ–ΈνŠΈκ°€ ESTABLISHED 둜 μ „ν™˜ν•œλ‹€.
    4. ν΄λΌμ΄μ–ΈνŠΈκ°€ ACK λ₯Ό 보내고 응닡 받은 μ„œλ²„λ„ ESTABLISHED 둜 μ „ν™˜ν•œλ‹€.
    5. λ‘˜ λͺ¨λ‘ ESTABLISHED 인 μƒνƒœμ—μ„œ data λ₯Ό μ£Όκ³  받을 수 μžˆλ‹€. 1.2 TCP μ—°κ²° 수립 & Passive vs. Active

2.2 Data Communication

2.2.0 TCP Window

  • TCP Window λž€ 각 peer κ°€ μž„μ‹œλ‘œ 데이터λ₯Ό 받을 수 μžˆλŠ” 버퍼이닀. μ‘μš© ν”„λ‘œκ·Έλž¨ μΈ‘μ—μ„œ 버퍼λ₯Ό 읽어가면 clear ν•œλ‹€. μ„Έκ·Έλ¨ΌνŠΈμ˜ Window 헀더 ν•„λ“œλ‘œ 남은 Window size λ₯Ό μƒλŒ€νŽΈ peer μ—κ²Œ μ•Œλ €μ€„ 수 μžˆλ‹€. 남은 TCP Window μ‚¬μ΄μ¦ˆκ°€ 0에 κ°€κΉŒμ›Œ 지면 버퍼가 λΉ„μ›Œμ‘Œλ‹€λŠ” μ—…λ°μ΄νŠΈκ°€ 갈 λ•Œ κΉŒμ§€ 전솑이 μ€‘λ‹¨λœλ‹€.
  • socket read κ°€ μ§€μ—°λ˜λ©΄ Window size κ°€ μž‘μ•„μ Έ μƒλŒ€νŽΈ peer 의 전솑이 μ€‘λ‹¨λœλ‹€.

2.3 Closing Connection

  • four-way-handshaking
TCP Peer A                                           TCP Peer B

1.  ESTABLISHED                                          ESTABLISHED

2.  (Close)
    FIN-WAIT-1  --> <SEQ=100><ACK=300><CTL=FIN,ACK>  --> CLOSE-WAIT

3.  FIN-WAIT-2  <-- <SEQ=300><ACK=101><CTL=ACK>      <-- CLOSE-WAIT

4.                                                       (Close)
    TIME-WAIT   <-- <SEQ=300><ACK=101><CTL=FIN,ACK>  <-- LAST-ACK

5.  TIME-WAIT   --> <SEQ=101><ACK=301><CTL=ACK>      --> CLOSED

6.  (2 MSL)
    CLOSED
  • TIME_WAIT & CLOSE_WAIT
    • μ„œλ²„ (μœ„ κ·Έλ¦Όμ—μ„œ Peer A) μΈ‘μ—μ„œ close ν•˜κ³  ν΄λΌμ΄μ–ΈνŠΈ ****(μœ„ κ·Έλ¦Όμ—μ„œ Peer B) μΈ‘μ—μ„œ ACK λ₯Ό 보내기 μ „ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ’…λ£Œν•΄ 버릴 경우 μ„œλ²„ μΈ‘μ—μ„œ λ‹΅μž₯을 받지 λͺ»ν–ˆκΈ° λ•Œλ¬Έμ— TIME_WAIT μƒνƒœμ— 걸리게 λœλ‹€. ν”„λ‘œκ·Έλž¨ μƒμ—μ„œ TIME-WAIT μƒνƒœλ₯Ό μ²˜λ¦¬ν•˜κΈ° μ–΄λ ΅κ³ , 재 연결을 μœ„ν•΄μ„œλŠ” 2MSL (Maximum Segment Lifetime) 만큼 κΈ°λ‹€λ €μ•Ό ν•˜λ―€λ‘œ μ„œλ²„ 츑의 close λŠ” μ‹ μ€‘ν•˜κ²Œ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.
    • μ–΄μ©” 수 μ—†λŠ” 경우 shutdown 을 μ΄μš©ν•΄ socket 의 write λΆ€ν„° λ‹«λŠ”λ‹€.

2.3.0 CLOSE_WAIT & TIME_WAIT

  • CLOSE 절차 쀑 νŠΉμ • μƒνƒœμ—μ„œ pending 되면 정상적인 연결이 이루어지지 λͺ»ν•˜λŠ” λ¬Έμ œκ°€ 생길 수 μžˆλ‹€.
  1. CLOSE_WAIT
    • passive close ν•˜λŠ” 경우, active close ν•œ μƒλŒ€νŽΈ peer κ°€ 보낸 FIN 을 λ°›κ³  close ν•˜κΈ° μ „ μƒνƒœμ΄λ‹€.
    • CLOSE_WAIT 은 정상적인 close μš”μ²­μœΌλ‘œ μ²˜λ¦¬ν•˜λŠ” FIN_WAIT λ‚˜ TIME_WAIT κ³ΌλŠ” λ‹€λ₯΄κ²Œ 일정 μ‹œκ°„μ΄ μ§€λ‚˜λ„ 사라지지 μ•ŠμœΌλ―€λ‘œ ν”„λ‘œμ„ΈμŠ€μ˜ μ’…λ£Œλ‚˜ λ„€νŠΈμ›Œν¬ μž¬μ‹œμž‘μœΌλ‘œ ν•΄κ²°ν•΄μ•Ό ν•œλ‹€.
    • Brilliant Server μ—μ„œλŠ” kqueue TIMER 이벀트λ₯Ό μ΄μš©ν•˜μ—¬ 일정 μ‹œκ°„μ΄ μ§€λ‚˜λ©΄ λͺ…μ‹œμ μœΌλ‘œ close μš”μ²­μ„ λ³΄λ‚΄λŠ” λ°©λ²•μœΌλ‘œ ν•΄κ²°ν•˜μ˜€λ‹€.
      if (events[i].filter == EVFILT_TIMER) {
              ClearConnectionResources(events[i].ident);
      }
      TIMER event 의 ident 와 바인딩 된 μ†ŒμΌ“ 의 fd λ₯Ό λ™μΌν•˜κ²Œ μ„€μ •ν•˜μ—¬ ClearConnectionResources μ—μ„œ close λ₯Ό ν˜ΈμΆœν•˜κ³  μžμ›μ •λ¦¬ν•œλ‹€.
  2. TIME_WAIT
    • active close ν›„ μƒλŒ€νŽΈμ˜ FIN을 κΈ°λ‹€λ¦¬λŠ” μƒνƒœμ΄λ‹€. 2MSL 이 μ§€λ‚œ 후에 CLOSED μƒνƒœκ°€ λ˜μ–΄μ•Ό λ‹€μ‹œ ν•΄λ‹Ή ν¬νŠΈμ— 바인딩 ν•  수 μžˆλ‹€.
    • TIME_WAIT 이 μ—†κ³  λ°”λ‘œ 연결이 λ‹«νžˆλŠ” 경우 λ¬Έμ œλ˜λŠ” 상황
      1. close λ˜λŠ” μ†ŒμΌ“μ˜ send 버퍼에 아직 보내지 μ•Šμ€ 데이터가 λ‚¨μ•„μžˆμ„ 수 μžˆλ‹€.
      2. μƒλŒ€νŽΈ peer μ—μ„œ 보내고 아직 λ„λ‹¬ν•˜μ§€ λͺ»ν•œ 데이터가 μžˆμ„ 수 μžˆλ‹€. μƒλŒ€νŽΈμ€ 아직 ACK 을 받지 λͺ»ν–ˆκΈ° λ•Œλ¬Έμ— 계속 μž¬μ „μ†‘μ„ μ‹œλ„ν•˜κ±°λ‚˜ 데이터 손싀이 μΌμ–΄λ‚œλ‹€.
      3. μƒλŒ€νŽΈ peer κ°€ LAST_ACKμƒνƒœμ—μ„œ 보낸 FIN 을 받을 수 μ—†μœΌλ―€λ‘œ 응닡도 쀄 수 μ—†λ‹€. μƒλŒ€νŽΈμ€ 아직 ACK 을 받지 λͺ»ν–ˆκΈ° λ•Œλ¬Έμ— 계속 μž¬μ „μ†‘μ„ μ‹œλ„ν•˜κ±°λ‚˜ 데이터 손싀이 μΌμ–΄λ‚œλ‹€.
    • TIME_WAIT 이 λ‚¨μ•„μžˆμ–΄λ„ μƒˆ bind λ₯Ό ν•˜κ³  싢은 경우 setsockopt λ₯Ό μ΄μš©ν•  수 μžˆλ‹€.
      1. SO_LINGER λ₯Ό μ‚¬μš©ν•œ 방법
        • 일반적인 경우 close 이후에 μœ„ κ²½μš°λ“€μ΄ λͺ¨λ‘ μ²˜λ¦¬λ˜μ§€λ§Œ l_linger λ₯Ό 맀우 μž‘κ²Œ μ„€μ •ν•˜μ—¬ SO_LINGER λ₯Ό μ‚¬μš©ν•˜λ©΄ 데이터 손싀 뿐만 μ•„λ‹ˆλΌ RST 컨트둀 λΉ„νŠΈλ₯Ό μ΄μš©ν•˜μ—¬ μ†ŒμΌ“μ„ μ’…λ£Œν•˜λ―€λ‘œ μƒλŒ€νŽΈμ—μ„œ Connection reset by peer 였λ₯˜λ₯Ό λ°œμƒμ‹œν‚¬ 수 μžˆλ‹€.
      2. SO_REUSEADDR 을 μ‚¬μš©ν•œ 방법
        • TIME_WAIT μƒνƒœμ—μ„œλ„ μƒˆλ‘œμš΄ socket 을 같은 μ£Όμ†Œμ— bind ν•  수 있게 ν•œλ‹€.

3 I/O 닀쀑 처리

3.0 I/O Multiplexing μ΄λž€

  • I/O Multiplexing 은 ν•˜λ‚˜μ˜ event loop μ—μ„œ μ—¬λŸ¬κ°œμ˜ I/O events λ₯Ό μ²˜λ¦¬ν•˜λŠ” 방식이닀.

  • 각 I/O λŠ” non-block 으둜 이루어지며 I/O μž‘μ—…μ΄ μΌμ–΄λ‚˜λŠ” FD 듀을 κ°μ‹œν•˜λŠ” μ‹œμŠ€ν…œ 콜(select, poll, epoll, kqueue … ) 을 ν™œμš©ν•˜μ—¬ FD 에 event 의 λ°œμƒ μ—¬λΆ€λ₯Ό κ°μ‹œν•˜κ³ , λ§Œμ•½ μ΄λ²€νŠΈκ°€ μžˆλ‹€λ©΄ μ μ ˆν•œ μž‘μ—…μ„ ν•΄μ•Όν•œλ‹€.

  • kqueue λ₯Ό 예둜 λ“€λ©΄, non-block I/O λ₯Ό 호좜 ν•œ λ’€ kevent 둜 block 을 μ‹œν‚€κ³ , FD 에 λ°œμƒν•œ event κ°€ μžˆλŠ”μ§€ ν™•μΈν•œλ‹€. kevent λŠ” ν•˜λ‚˜μ˜ FD 뿐만 μ•„λ‹ˆλΌ, μ—¬λŸ¬κ°œμ˜ FD 에 λŒ€ν•œ event λ₯Ό 감지할 수 μžˆμ–΄μ„œ μ—¬λŸ¬μ˜ I/O λ₯Ό ν•œ ν”„λ‘œμ„ΈμŠ€μ—μ„œ 관리할 수 있게 λœλ‹€.

3.0.1 NON-BLOCKING I/O

  • κΈ°μ‘΄ read/write ν˜ΈμΆœμ€ ν”„λ‘œμ„ΈμŠ€λ₯Ό block μ‹œν‚€κ³  I/O μž‘μ—…μ΄ μ™„λ£Œλ˜κΈ°λ₯Ό κΈ°λ‹€λ¦¬μ§€λ§Œ, non-block I/O λŠ” read/write ν˜ΈμΆœμ‹œ read/writeκ°€ κ°€λŠ₯ν•˜λ‹€λ©΄ μž‘μ—…μ΄ μˆ˜ν–‰λ˜κ³ , 그렇지 μ•Šλ‹€λ©΄ -1 을 λ°˜ν™˜ν•˜λ©° errnoκ°€EAGAIN|EWOULDBLOCK 둜 μ„€μ •λœλ‹€.

non-blocking I/O μ„€λͺ…

좜처 : https://ecsimsw.tistory.com/entry/Web-server-with-socket-API

3.1 kqueue & kevent

3.1.1 kqueue 와 kevent λž€

  • kqueue 와 kevent λŠ” kernel event notification mechanism 이며 각각 kernel queue, kernel event λ₯Ό λœ»ν•œλ‹€.

    #include <sys/types.h>
    #include <sys/event.h>
    #include <sys/time.h>
    
    int kqueue(void);
    
    int kevent(int kq, const struct kevent *changelist, int nchanges,
    	struct kevent *eventlist, int nevents, const struct timespec *timeout);
    
    struct kevent {
           uintptr_t       ident;          /* identifier for this event */
           int16_t         filter;         /* filter for event */
           uint16_t        flags;          /* general flags */
           uint32_t        fflags;         /* filter-specific flags */
           intptr_t        data;           /* filter-specific data */
           void            *udata;         /* opaque user data identifier */
    };
    
    EV_SET(&kev, ident, filter, flags, fflags, data, udata);
  • kqueue() μ‹œμŠ€ν…œ μ½œμ€ μƒˆλ‘œμš΄ kqueue FD λ₯Ό λ°˜ν™˜ν•œλ‹€. 이 FD λŠ” filters 라고 ν•˜λŠ” kernel code 의 κ²°κ³Όλ₯Ό 기반으둜 kernel event κ°€ λ°œμƒν•˜κ±°λ‚˜ 쑰건을 μΆ©μ‘±ν•˜λ©΄, μ‚¬μš©μžμ—κ²Œ μ•Œλ €μ£ΌλŠ” 일반적인 방법을 μ œκ³΅ν•œλ‹€. kqueue fd status

  • kevent κ΅¬μ‘°μ²΄λŠ” (ident, filter, udata(optional) ) νŠœν”Œλ‘œ μ‹λ³„λ˜λ©° kevent ꡬ쑰체에 ν•΄λ‹Ή νŠœν”Œμ— λŒ€ν•΄ μ•Œλ¦Όμ„ 받을 쑰건을 μ§€μ •ν•œλ‹€. I/O event의 경우 ident 둜 FD κ°€ λ“€μ–΄κ°€κ³ , filter 에 EVFILT_READ, EVFILT_WRITE 값을 λ„£μ–΄μ„œ read/write 이벀트λ₯Ό 등둝 ν•  수 μžˆλ‹€.

    void HttpServer::InitKqueue(void) {
      kq_ = kqueue(); // kqueue 생성
      if (kq_ == -1) {
        PRINT_ERROR("HttpServer : kqueue failed : " << strerror(errno));
        exit(EXIT_FAILURE);
      }
    	// kevent ꡬ쑰체 동적 ν• λ‹Ή
      struct kevent* sock_ev =
          new (std::nothrow) struct kevent[passive_sockets_.size()];
      if (sock_ev == NULL) {
        PRINT_ERROR("HttpServer : failed to allocate memory");
        exit(EXIT_FAILURE);
      }
      int i = 0;
      for (ListenerMap::const_iterator it = passive_sockets_.begin();
           it != passive_sockets_.end(); ++it) {
    		// kevent ꡬ쑰체 λ°°μ—΄ μ΄ˆκΈ°ν™” (ident: fd)
        EV_SET(&sock_ev[i++], it->first, EVFILT_READ, EV_ADD, 0, 0, NULL);
      }
    	// kevent에 changlist, nchangesλ₯Ό 인자둜 λ„˜κ²¨ 이벀트 등둝
      if (kevent(kq_, sock_ev, passive_sockets_.size(), NULL, 0, NULL) == -1) {
        PRINT_ERROR("HttpServer : failed to listen : " << strerror(errno));
        exit(EXIT_FAILURE);
      }
      delete[] sock_ev;
    }
  • kevent() ν•¨μˆ˜λŠ” changelist 에 κ°μ‹œν•  kevent ꡬ쑰체의 포인터λ₯Ό λ°›μ•„ 이벀트λ₯Ό λ“±λ‘ν•œλ‹€.

    while (true) {
    	// μ΄λ²€νŠΈκ°€ λ°œμƒν•  λ•Œ κΉŒμ§€ block
      int number_of_events = kevent(kq_, NULL, 0, events, MAX_EVENTS, NULL);
      if (number_of_events == -1) {
        PRINT_ERROR("HttpServer : kevent failed : " << strerror(errno));
      }
      for (int i = 0; i < number_of_events; ++i) {
        if (events[i].filter == EVFILT_READ) {
    			/* READ 이벀트 λ°œμƒ, read μž‘μ—… μˆ˜ν–‰ν•˜κΈ° */
        } else if (events[i].filter == EVFILT_WRITE) {
          /* Write 이벀트 λ°œμƒ, write μž‘μ—… μˆ˜ν–‰ν•˜κΈ° */
        }
      }
    }
  • eventlist μ—λŠ” 이벀트 λ°œμƒμ‹œ 이벀트 데이터λ₯Ό λ°›μ•„μ˜¬ kevent ꡬ쑰체의 포인터λ₯Ό λ°›κ³ , 이벀트 λ°œμƒμ‹œ λ°œμƒν•œ 이벀트의 κ°œμˆ˜κ°€ λ°˜ν™˜λ˜κ³ , eventlist 에 넣은 kevent ꡬ쑰체 에 데이터가 λ‹΄κ²¨μ˜¨λ‹€.

  • I/O 의 경우 kevent ꡬ쑰체의 ident λ₯Ό FD 둜 λ„˜κΈ°κ³ , filter 에 EVFILT_READ|WRITE λ₯Ό μ£Όλ©΄ λ‹€μŒκ³Ό 같은 κ²½μš°μ— μ΄λ²€νŠΈκ°€ λ°œμƒν•œλ‹€.

    • READ 의 경우 FD 에 읽을 수 μžˆλŠ” 데이터가 μžˆμ„ λ•Œ
    • WRITE 의 경우 FD 에 데이터λ₯Ό μ“Έ 수 μžˆμ„ λ•Œ
  • μ΄λ²€νŠΈκ°€ λ°œμƒν•œ 경우 μ μ ˆν•œ READ / WRITE ν˜ΈμΆœμ„ ν•΄μ£Όλ©΄, non-block I/O μž„μ—λ„ μ μ ˆν•˜κ²Œ I/O λ₯Ό 처리 ν•  수 μžˆλ‹€.

3.1.2 kqueue λ₯Ό μ„ νƒν•œ 이유

kqueue vs select vs poll

  • select μž‘λ™ 방식

    #include <sys/select.h>
    
    int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds,
    fd_set *restrict errorfds, struct timeval *restrict timeout);
    • select() λŠ” 인자둜 κ°μ‹œν•  fd 의 개수(nfds)λ₯Ό λ°›λŠ”λ‹€.
    • select() 호좜 μ‹œ (0 ~ nfds-1)개의 배열을 μˆœνšŒν•˜λ©° 이벀트λ₯Ό νƒμ§€ν•œλ‹€. $O(nfds)$
    • 이벀트 λ°œμƒμ‹œ 데이터가 λ³€κ²½λœ 파일의 κ°œμˆ˜κ°€ λ°˜ν™˜λ˜μ–΄, 배열을 λ‹€μ‹œ μˆœνšŒν•˜λ©° μ–΄λ–€ fd μ—μ„œ μ΄λ²€νŠΈκ°€ λ°œμƒν–ˆλŠ”μ§€ μ°Ύμ•„μ•Ό ν•œλ‹€.
    • nfds κ°€ 1024 λ₯Ό λ„˜μ„ 수 μ—†λ‹€.
    • fd λ§ˆλ‹€ bitmasking 을 ν™œμš©ν•˜μ—¬ 3bit 만으둜 ν•œ fd 의 μƒνƒœλ₯Ό 좔적할 수 μžˆλ‹€.
  • poll μž‘λ™ 방식

    #include <poll.h>
    
    int poll(struct pollfd fds[], nfds_t nfds, int timeout);
    • poll() 은 fd 와 이벀트 정보가 λ‹΄κΈ΄ pollfd λ°°μ—΄κ³Ό, μ‹€μ œλ‘œ κ°μ‹œν•˜λŠ” fd 의 수인 nfds λ₯Ό 인자둜 λ°›λŠ”λ‹€.
    • select() 에선 nfds μ‚¬μ΄μ¦ˆμ˜ 배열을 μˆœνšŒν•˜λ©° μ΄λ²€νŠΈκ°€ λ°œμƒν•œ fd λ₯Ό μ°Ύμ•„μ•Ό ν–ˆμ§€λ§Œ, poll() μ‹€μ œλ‘œ κ°μ‹œν•˜λŠ” fd 의 개수만큼 순회λ₯Ό ν•  수 μžˆλ‹€. $O(fd_count)$
    • κ°μ‹œ κ°€λŠ₯ν•œ fd 의 μˆ˜κ°€ λ¬΄μ œν•œμ΄λ‚˜, ν•œ ꡬ쑰체당 64bit 의 크기λ₯Ό κ°€μ Έ λ§Žμ€ 이벀트λ₯Ό λ‹€λ£° 경우 select() 보닀 μ„±λŠ₯이 λ–¨μ–΄μ§ˆ 수 μžˆλ‹€.
  • select(), poll() 의 문제점

    • select(), poll() 은 호좜 ν•  λ•Œ λ§ˆλ‹€, 전체 fd 배열을 인자둜 λ„˜κ²¨μ•Ό ν•˜λ©°, 이 배열이 user-space μ—μ„œ kernel-space 둜 λ³΅μ‚¬λ λ•Œ μƒλ‹Ήν•œ μ˜€λ²„ν—€λ“œκ°€ μ‘΄μž¬ν•œλ‹€. (95% λŠ” λΆˆν•„μš”ν•œ 볡사)
    • kernel μ—μ„œ μ΄λ²€νŠΈκ°€ λ°œμƒν•˜λ©΄, kernel-space μ—μ„œλŠ” 이미 μ΄λ²€νŠΈκ°€ λ°œμƒν•œ fd λ₯Ό μ•„λŠ”λ°λ„ λΆˆκ΅¬ν•˜κ³  user-space μ—μ„œ λ°œμƒν•œ 이벀트λ₯Ό μ°ΎκΈ° μœ„ν•΄ 배열을 μˆœνšŒν•΄μ•Ό ν•œλ‹€.
  • kqueue(), kevent() 의 μž₯점

    • kevent λŠ” kernel μ—μ„œ μ‹€μ œλ‘œ μ΄λ²€νŠΈκ°€ λ°œμƒν•œ fd list 만 λ°˜ν™˜ν•˜μ—¬, application μ—μ„œ 이벀트λ₯Ό λ°”λ‘œ 좔적할 수 μžˆλ‹€.
    • I/O event 뿐만 μ•„λ‹ˆλΌ process event, signal event, timer event 등을 등둝 ν•  수 μžˆλ‹€.
  • kqueue(), kevent() 의 단점

    • FreeBSD 계열에 ν•œμ •λœ μ‹œμŠ€ν…œ 콜 μ΄λΌμ„œ ν˜Έν™˜μ„±μ΄ 쒋지 μ•Šλ‹€. 도컀λ₯Ό ν™œμš©ν•˜μ—¬ λ¦¬λˆ…μŠ€μ—μ„œ μ‹€ν–‰ν•˜κ³  μ‹Άμ—ˆλŠ”λ° μ‹€νŒ¨ν–ˆλ‹€.

3.2 μ΅œμ ν™”

3.2.0 writev 와 send

  • κΈ°μ‘΄μ—” send λ₯Ό μ΄μš©ν•˜μ—¬ response λ₯Ό λ³΄λƒˆμœΌλ‚˜ response 의 header 와 content κ°€ λΆ„λ¦¬λ˜μ–΄ μžˆλŠ” μƒν™©μ—μ„œ send λ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„  content 의 λΆˆν•„μš”ν•œ 볡사가 μΌμ–΄λ‚˜λŠ” λ¬Έμ œκ°€ μžˆμ—ˆλ‹€.

    #include <sys/uio.h>
    ssize_t writev(int fildes, const struct iovec *iov, int iovcnt);
    struct iovec {
       char   *iov_base;  /* Base address. */
       size_t iov_len;    /* Length. */
    };
  • writev λ₯Ό μ‚¬μš©ν•˜λ©΄, header 와 content κ°€ λ‹€λ₯Έ 버퍼에 μžˆλ”λΌλ„, iovec ꡬ쑰체에 header 와 content 의 μ£Όμ†Œλ₯Ό λ„˜κ²¨μ£Όλ©΄, ν•˜λ‚˜μ˜ λ²„νΌλ‘œ write ν•˜λŠ” 것과 같은 νš¨κ³Όκ°€ μžˆλ‹€. λ”°λΌμ„œ λΆˆν•„μš”ν•œ 볡사도 μΌμ–΄λ‚˜μ§€ μ•Šκ³ , write μ‹œμŠ€ν…œ μ½œλ„ 쀄일 수 μžˆλ‹€.

3.2.1 Low water mark(SO_SNDLOWAT) 와 writev

  • λ‹€μˆ˜μ˜ ν΄λΌμ΄μ–ΈνŠΈκ°€ μ •μ˜ν•œ BUFFER_SIZE 보닀 큰 νŒŒμΌμ„ μš”μ²­ ν–ˆμ„ λ•Œ, content κ°€ content-length 보닀 적게 μ „μ†‘λ˜λŠ” λ¬Έμ œκ°€ μžˆμ—ˆλ‹€. 이λ₯Ό setsocketopt ν•¨μˆ˜λ‘œ socket 에 SO_SNDLOWAT μ˜΅μ…˜μ„ μ€˜μ„œ ν•΄κ²°ν–ˆλ‹€.
    setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &buf_size, sizeof(int))
  • SO_SNDLOWAT μ˜΅μ…˜μ€ non-block socket μ—μ„œ socket buffer 에 output 을 μœ„ν•œ buf_size 만큼의 bytes κ°€ λΉ„μ–΄μžˆκ±°λ‚˜, ν•œλ²ˆμ— λͺ¨λ“  데이터λ₯Ό socket buffer 에 write ν•  수 μžˆμ„ λ•Œ (λ°μ΄ν„°μ˜ 크기가 buf_size 보닀 μž‘μ„ λ•Œ) send() κ°€ κ°€λŠ₯해지며, 그렇지 μ•ŠμœΌλ©΄ send() κ°€ 아무 데이터도 μ „μ†‘ν•˜μ§€ μ•Šκ³  μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.
  • kevent 의 EVFILT_WRITE 에 SO_SNDLOWAT μ˜΅μ…˜μ΄ 적용된 socket 을 λ“±λ‘ν•˜κ²Œ 되면, ν•΄λ‹Ή socket 에 buf_size 만큼 socket buffer 에 write ν•  수 μžˆμ„λ•Œ μ΄λ²€νŠΈκ°€ λ°œμƒν•˜κ²Œ λœλ‹€.
  • buf_size λ₯Ό SEND_BUFFER_SIZE 인 32kb 둜 μ •μ˜ ν–ˆμœΌλ‚˜, μ„œλ²„κ°€ λ„ˆλ¬΄ λ§Žμ€ μš”μ²­μ„ λ°›λŠ” μƒνƒœμ—μ„œ μ—¬μ „νžˆ content κ°€ 덜 μ „μ†‘λ˜λŠ” λ¬Έμ œκ°€ λ°œμƒν•˜μ—¬ SEND_BUFFER_SIZE 의 1.5배둜 μ„€μ •ν•˜μ—¬ μ–΄λŠμ •λ„ ν•΄κ²°ν–ˆλ‹€.

4 HTTP

4.0 HTTP general

  • HTTP λ©”μ‹œμ§€λŠ” μ•„λž˜μ˜ μ„Ήμ…˜λ“€λ‘œ μ΄λ€„μ Έμžˆλ‹€.
    • control data (request line / response line)
    • header table
    • octet-stream content
    • trailer table
  • HTTP λ©”μ‹œμ§€μ˜ μ‹œμž‘κ³Ό 끝을 framing 이라고 ν•˜κ³ , framing 은 μ•„λž˜μ™€ 같은 ν˜•μ‹μœΌλ‘œ κ²°μ •λœλ‹€.
    • body κ°€ μ—†λŠ” 경우
      • (μ‹œμž‘) control data
      • header
      • CRLF CRLF (끝)
    • body κ°€ μžˆλŠ” 경우
      • (μ‹œμž‘) control data
      • header (Content-Length: (positive integer)/Transfer-Encoding: chunked)
      • CRLF CRLF
      • Content-Length 길이 만큼의 octet-stream bytes content / chunked λ©”μ‹œμ§€κ°€ 끝날 λ•ŒκΉŒμ§€ (끝)
    • body length κ²°μ •
      1. 1xx, 204 OR 304 status code λ₯Ό κ°–λŠ” 응닡은 header fields + CRLF + CRLF 둜 λλ‚œλ‹€.
      2. Transfer-Encoding & Content-Length λ‘˜ λ‹€ μžˆλŠ” λ©”μ‹œμ§€κ°€ μˆ˜μ‹ λœλ‹€λ©΄ Transfer-Encoding 이 Content-Length 의 값을 override ν•œλ‹€. 이런 λ©”μ‹œμ§€λŠ” request smuggling/ response splitting 의 μ‹œλ„μΌ 수 있고 μ—λŸ¬λ‘œ μ²˜λ¦¬λ˜μ–΄μ•Όν•œλ‹€.
      3. Transfer-Encoding header field κ°€ 있고
        1. chunked κ°€ λ§ˆμ§€λ§‰ encoding 일 λ•Œ, λ©”μ‹œμ§€ body length λŠ” transfer coding 이 끝났닀고 μ•Œλ €μ€„ λ•ŒκΉŒμ§€ μ½μœΌλ©΄μ„œ κ²°μ •ν•œλ‹€. (chunked-size 에 0 이 올 λ•ŒκΉŒμ§€ μ½μœΌλΌλŠ” 말인 것 κ°™λ‹€.)
        2. 응닡 λ©”μ‹œμ§€μ˜ 경우, chunked κ°€ λ§ˆμ§€λ§‰ encoding 이 아닐 λ•Œ, μ„œλ²„κ°€ 연결을 λŠμ„ λ•ŒκΉŒμ§€ μ½λŠ”λ‹€.
        3. μš”μ²­ λ©”μ‹œμ§€μ˜ 경우, chunked κ°€ λ§ˆμ§€λ§‰ encoding 이 아닐 λ•Œ, Bad Request (400) & 연결을 λŠλŠ”λ‹€.
      4. Transfer-Encoding 이 μ—†κ³ , Content-Length κ°€ μœ νš¨ν•˜μ§€ μ•ŠμœΌλ©΄, λ©”μ‹œμ§€ framing 이 μœ νš¨ν•˜μ§€ μ•Šλ‹€ (a 경우 외에). μ„œλ²„λŠ” Bad Request (400) & 연결을 λŠλŠ”λ‹€.
        1. Content-Length: 400,400,400,400 (같은 숫자 & β€˜,’ separated list 이면 ν•΄λ‹Ή λ°˜λ³΅λ˜λŠ” 숫자둜 지정)
      5. Transfer-Encoding 이 μ—†κ³ , Content-Length κ°€ μœ νš¨ν•œ 경우, Content-Length 에 λͺ…μ‹œλœ μˆ˜κ°€ body length. Content-Length 에 λͺ…μ‹œλœ 수 만큼의 octets κ°€ μˆ˜μ‹ λ˜κΈ° μ „ λ§Œμ•½ μ†‘μ‹ μžκ°€ 연결을 λŠκ±°λ‚˜ μˆ˜μ‹ μžκ°€ time out 되면 ν•΄λ‹Ή λ©”μ‹œμ§€λŠ” λ―Έμ™„μ„±μœΌλ‘œ (incomplete) 으둜 보고 연결을 λŠμ–΄μ•Όν•œλ‹€ (MUST).
      6. μš”μ²­ λ©”μ‹œμ§€ & μœ„μ˜ μ–΄λ–€ μΌ€μ΄μŠ€μ—λ„ ν•΄λ‹Ήν•˜μ§€ μ•ŠμœΌλ©΄ body length λŠ” 0.
      7. 응닡 λ©”μ‹œμ§€ & μœ„μ˜ μ–΄λ–€ μΌ€μ΄μŠ€μ—λ„ ν•΄λ‹Ήν•˜μ§€ μ•ŠμœΌλ©΄ μ„œλ²„κ°€ 응닡을 close ν•˜κΈ° 전에 보낸 만큼이 body length.
      • 연결이 λλ‚˜μ„œ λ©”μ‹œμ§€κ°€ λλ‚œ 건지, λ„€νŠΈμ›Œν¬ μ—λŸ¬λ‘œ 연결이 λŠκ²ΌλŠ”μ§€ νŒλ³„ν•˜λŠ”κ²Œ μ–΄λ ΅κΈ° λ•Œλ¬Έμ— μ„œλ²„λŠ” encoding ν˜Ήμ€ length λͺ…μ‹œλ₯Ό κΌ­ ν•΄μ€˜μ•Όν•œλ‹€ (SHOULD).
        • close-delimiting 은 HTTP/1.0 과의 ν•˜μœ„ ν˜Έν™˜μ„±μ„ μœ„ν•΄ μ§€μ›ν•œλ‹€.
        • μš”μ²­ λ©”μ‹œμ§€λŠ” μ ˆλŒ€ close-delimiting 을 ν•˜μ§€ μ•ŠλŠ”λ‹€. 항상 Content-Length / Transfer-Encoding 으둜 body 의 끝을 μ•Œλ €μ€€λ‹€ (MUST).
      • μš”μ²­ λ©”μ‹œμ§€μ— body κ°€ μžˆλŠ”λ° Content-Length κ°€ μ—†λŠ” 경우 μ„œλ²„λŠ” Length Required (411) 둜 응닡할 수 μžˆλ‹€ (MAY).
      • μš”μ²­ λ©”μ‹œμ§€μ— Transfer-Encoding 이 μžˆλŠ”λ° chunked μ΄μ™Έμ˜ coding 이 적용됐고, λ©”μ‹œμ§€ 길이λ₯Ό μ•Œ 수 μžˆλ‹€λ©΄ ν΄λΌμ΄μ–ΈνŠΈλŠ” chunked λ₯Ό μ‚¬μš©ν•˜λŠ” 것보닀 μœ νš¨ν•œ Content-Length λ₯Ό λͺ…μ‹œν•˜λŠ” κ±Έ μš°μ„ ν•΄μ•Όν•œλ‹€ (SHOULD). μ„œλ²„λ“€μ΄ chunked coding 을 해석할 수 μžˆμ–΄λ„ Length Required (411) 둜 응닡할 μˆ˜λ„ 있기 λ•Œλ¬Έμ΄λ‹€.

4.1 HTTP/1.1

4.1.0 Host 헀더 ν•„λ“œλ‘œ name-based λΌμš°νŒ… 지원

  • 같은 IP + port μ•ˆμ—μ„œ μ—¬λŸ¬κ°œμ˜ Server ν˜ΈμŠ€νŒ…μ΄ κ°€λŠ₯ν•΄μ‘Œλ‹€. Name-based virtual server λŠ” μš”μ²­μ˜ Host 헀더 ν•„λ“œ κ°’μœΌλ‘œ μš”μ²­μ΄ ν–₯ν•˜λŠ” Server λ₯Ό λΌμš°νŒ…ν•œλ‹€.

  • μ•„λž˜μ™€ 같이 μ„œλ²„κ°€ μ„€μ •λ˜μ–΄ μžˆμ„ λ•Œ, μš”μ²­μ˜ Host 헀더 ν•„λ“œ 값이 ghan 이면 첫번째 Server, yongjule λ©΄ μ„Έλ²ˆμ§Έ Server 둜 λΌμš°νŒ…λœλ‹€.

    server {
    	listen 80
    	server_name ghan
    	...
    }
    
    server {
    	listen 80
    	server_name jiskim
    	...
    }
    
    server {
    	listen 80
    	server_name yongjule
    	...
    }

4.1.1 Transfer-Encoding: chunked

  • HTTP/1.1 은 Transfer-Encoding: chunked 헀더 ν•„λ“œλ‘œ content 전체 길이λ₯Ό λͺ¨λ₯΄λŠ” content stream 을 length-delimited buffer 의 μ—°μ†μœΌλ‘œ 전솑할 수 있게 ν•΄μ€€λ‹€.

    chunked-body   = *chunk
    last-chunk
    trailer-section
    CRLF
    
    chunk          = chunk-size [ chunk-ext ] CRLF
    chunk-data CRLF
    chunk-size     = 1*HEXDIG
    last-chunk     = 1*("0") [ chunk-ext ] CRLF
    
    chunk-data     = 1*OCTET ; a sequence of chunk-size octets
  • μ•„λž˜ μ˜ˆμ‹œμ™€ 같이, 16μ§„μˆ˜λ‘œ λͺ…μ‹œλœ chunk-size λ‹€μŒ 쀄에 ν•΄λ‹Ή μ‚¬μ΄μ¦ˆ 만큼의 octet stream 이 λ”°λ₯Έλ‹€. chunk-size 0 으둜 transfer coding 의 끝을 μ•Œλ¦¬κ³ , trailer section 이 μ΄μ–΄μ§ˆ μˆ˜λ„ 있고, CRLF 둜 body 의 끝을 ν‘œμ‹œν•œλ‹€.

    HTTP/1.1 200 OK
    Content-Type: text/plain
    Transfer-Encoding: chunked
    
    4\r\n
    ghan\r\n
    6\r\n
    jiskim\r\n
    8\r\n
    yongjule\r\n
    0\r\n
    \r\n
  • μˆ˜μ‹ μžλŠ” chunked transfer coding 을 νŒŒμ‹±ν•  수 μžˆμ–΄μ•Όν•œλ‹€ (MUST).

  • μˆ˜μ‹ μžλŠ” 맀우 큰 chunk-size κ°€ 올 수 μžˆλ‹€λŠ” κ±Έ μ˜ˆμƒν•˜κ³ , overflow, precision loss 와 같은 νŒŒμ‹± μ—λŸ¬λ₯Ό λ°©μ§€ν•΄μ•Όν•œλ‹€ (MUST). (HTTP/1.1 은 μ œν•œμ„ μ„€μ •ν•˜μ§€ μ•Šμ•˜λ‹€.)

  • chunked 에 parameter κ°€ λΆ™μœΌλ©΄ μ—λŸ¬ (SHOULD).

  • chunk-ext κ°€ μ•„λž˜μ˜ 포맷으둜 chunk-size μ˜†μ— λ‚˜μ˜¬ μˆ˜λ„ μžˆλ‹€. μˆ˜μ‹ μžλŠ” 이해할 수 μ—†λŠ” (unrecognized) chunk extension 을 λ¬΄μ‹œν•΄μ•Όν•œλ‹€ (MUST). (λ‹€ 이해할 수 μ—†κΈ°λ‘œ ν•˜μžβ€¦ 문법 체크만 ν•˜μžβ€¦)

    chunk-ext      = *( BWS ";" BWS chunk-ext-name
    [ BWS "=" BWS chunk-ext-val ] )
    
    chunk-ext-name = token
    chunk-ext-val  = token / quoted-string
  • chunked coding 을 μ—†μ• λŠ” μˆ˜μ‹ μžλŠ” trailer fields λ₯Ό μœ μ§€ν• μ§€ 없앨지 μ •ν•  수 μžˆλ‹€ (MAY). μœ μ§€ν•œλ‹€λ©΄, header field μ™€λŠ” λ‹€λ₯Έ ꡬ쑰체에 μ €μž₯ν•΄μ•Όν•œλ‹€.

4.1.1 Connection Management

  • Persistence
    • HTTP/1.1 은 기본적으둜 persistent connection 을 μ‚¬μš©ν•˜λ©°, ν•˜λ‚˜μ˜ μ—°κ²° μ—μ„œ μ—¬λŸ¬κ°œμ˜ μš”μ²­κ³Ό 응닡을 μ£Όκ³  받을 수 μžˆλ‹€. μ΄λŠ” κ°€μž₯ μ΅œκ·Όμ— 받은 protocol version μ΄λ‚˜ Connection 헀더 ν•„λ“œμ— μ˜ν•΄ κ²°μ •λ˜λŠ”λ°, μ•„λž˜μ™€ 같은 μš°μ„ μˆœμœ„μ— μ˜ν•˜μ—¬ κ²°μ •λœλ‹€.
      1. Connection 헀더 ν•„λ“œμ— close κ°€ μžˆλ‹€λ©΄, ν˜„μž¬ 응닡 이후에 connection 은 더이상 μ§€μ†λ˜μ§€ μ•ŠλŠ”λ‹€. else;
      2. 받은 μš”μ²­μ΄ HTTP/1.1 이면 연결은 계속 μ§€μ†λœλ‹€. else;
      3. 받은 μš”μ²­μ΄ HTTP/1.0 이고 Connection 헀더 ν•„λ“œκ°€ keep-alive λ©΄ 연결은 계속 μ§€μ†λœλ‹€.
      4. ν˜„μž¬ μ—°κ²° 이후에 연결은 λ‹«νžŒλ‹€.
  • pipelining
    • persistent connection 을 μ§€μ›ν•˜λŠ” ν΄λΌμ΄μ–ΈνŠΈλŠ” μš”μ²­μ„ pipeline (응닡을 기닀리지 μ•Šκ³  μ—¬λŸ¬κ°œμ˜ μš”μ²­μ„ λ³΄λ‚΄λŠ”κ²ƒ)을 ν•  수 μžˆλ‹€. μ„œλ²„λŠ” pipeline 으둜 μ˜€λŠ” μš”μ²­μ΄ λͺ¨λ‘ μ•ˆμ „ν•œ method λ₯Ό 가진 μš”μ²­μ΄λΌλ©΄, 이λ₯Ό λ³‘λ ¬μ μœΌλ‘œ μ²˜λ¦¬ν•  수 μžˆμ§€λ§Œ 각 μš”μ²­μ— μƒμ‘ν•˜λŠ” 응닡을 받은 것과 같은 μˆœμ„œλ‘œ λ³΄λ‚΄μ€˜μ•Ό ν•œλ‹€.

5 Common Gateway Interface

5.0 Common Gateway Interface λž€

  • Common Gateway Interface (CGI) λŠ” HTTP Server κ°€ ν”Œλž«νΌμ— 상관 없이 μ™ΈλΆ€ ν”„λ‘œκ·Έλž¨μ„ μ‹€ν–‰μ‹œν‚¬ 수 있게 ν•΄μ£ΌλŠ” μΈν„°νŽ˜μ΄μŠ€λ‹€.
  • RFC 3875 에 규격이 μ •μ˜λ˜μ–΄ μžˆλ‹€.

5.1 CGI 와 Server 의 μ†Œν†΅

  • Server λŠ” CGI script λ₯Ό execve 둜 ν˜ΈμΆœν•˜λ©° μ•„λž˜μ˜ meta-variable 듀을 env 둜 μ„€μ •ν•΄μ€€λ‹€ (execve 의 μ„Έλ²ˆμ§Έ 인자둜 전달).
    "AUTH_TYPE=" // μ„œλ²„κ°€ μ‚¬μš©μž 인증에 μ‚¬μš©ν•˜λŠ” 방법.
    "CONTENT_LENGTH=" // μš”μ²­ λ©”μ‹œμ§€ body 의 크기λ₯Ό μ‹­μ§„μˆ˜λ‘œ ν‘œν˜„
    "CONTENT_TYPE=" // μš”μ²­ λ©”μ‹œμ§€ body 의 Internet Media Type
    "GATEWAY_INTERFACE=CGI/1.1" // server κ°€ μ μš©ν•˜λŠ” CGI version
    "PATH_INFO=" // Script-URI 의 script-path 에 이어 λ‚˜μ˜€λŠ” λΆ€λΆ„
    "PATH_TRANSLATED=" // PATH_INFO 기반으둜 ν•΄λ‹Ή resource 의 local μ ˆλŒ€ 경둜
    "QUERY_STRING=" // URL-encoded 검색/parameter λ¬Έμžμ—΄
    "REMOTE_ADDR=" // μš”μ²­μ„ λ³΄λ‚΄λŠ” client 의 λ„€νŠΈμ›Œν¬ μ£Όμ†Œ
    "REMOTE_HOST=" // μš”μ²­μ„ λ³΄λ‚΄λŠ” client 의 도메인
    "REMOTE_IDENT="
    "REMOTE_USER=" // μ‚¬μš©μž 인증을 μœ„ν•΄ client κ°€ μ œκ³΅ν•˜λŠ” μ‚¬μš©μž μ‹λ³„μž
    "REQUEST_METHOD=" // μš”μ²­ method
    "SCRIPT_NAME=" // μš”μ²­μ—μ„œ cgi script κΉŒμ§€μ˜ 경둜
    "SERVER_NAME=" // μ„œλ²„ λͺ…
    "SERVER_PORT=" // μ„œλ²„ 포트
    "SERVER_PROTOCOL=" // μ„œλ²„ ν”„λ‘œν† μ½œ
    "SERVER_SOFTWARE=" // μ„œλ²„ ν”„λ‘œκ·Έλž¨ λͺ…
  • CGI λŠ” ν‘œμ€€ 좜λ ₯에 CGI 응닡을 μž‘μ„±ν•˜κ³  EOF 둜 μ‘λ‹΅μ˜ 끝을 Server μ—κ²Œ μ•Œλ¦°λ‹€.
  • CGI 의 응닡을 λ°›κΈ° μœ„ν•΄ Server λŠ” execve 전에 pipe λ₯Ό μ—΄μ–΄ CGI 둜 λΆ€ν„° 응닡 받을 채널을 μ€€λΉ„ν•œλ‹€.

5.2 CGI 응닡

  • CGI 응닡은 header ν•„λ“œμ™€ body 둜 κ΅¬μ„±λœλ‹€.
  • header ν•„λ“œλŠ” CGI-field (Content-Type | Location | Status) + HTTP field (선택) + extension field (선택) 둜 이루어진닀.
  • body λŠ” EOF κΉŒμ§€ 쓰인 octet-stream 이닀.
  • CGI 응닡은 Document Response, Local Redirection Response, Client Redirection Response, Client Redirection Response with Document 둜 λ‚˜λ‰œλ‹€.

5.2.0 Document Response

document-response = Content-Type [ Status ] *other-field NL
                    response-body
  • 일반적인 λ¬Έμ„œ λ°˜ν™˜, Content-Type ν•„λ“œλŠ” ν•„μˆ˜, Status λŠ” μ—†μœΌλ©΄ 200 으둜 κ°„μ£Όν•œλ‹€.

5.2.1 Redirection Response

  • Location ν•„λ“œκ°€ ν•„μˆ˜μ΄λ‹€.
    Location        = local-Location | client-Location
    client-Location = "Location:" fragment-URI NL
    local-Location  = "Location:" local-pathquery NL
    fragment-URI    = absoluteURI [ "#" fragment ]
    fragment        = *uric
    local-pathquery = abs-path [ "?" query-string ]
    abs-path        = "/" path-segments
    path-segments   = segment *( "/" segment )
    segment         = *pchar
    pchar           = unreserved | escaped | extra
    extra           = ":" | "@" | "&" | "=" | "+" | "$" | ","
  1. Local Redirect

    local-redir-response = local-Location NL
    • CGI λŠ” Location ν•„λ“œ 값에 λ¦¬λ‹€μ΄λ ‰νŠΈ ν•  경둜λ₯Ό 적어쀀닀.
    • Server λŠ” κ·Έ 경둜둜 μš”μ²­μ΄ 온 κ²ƒμ²˜λŸΌ μš”μ²­μ„ μ²˜λ¦¬ν•œλ‹€.
  2. Client Redirect

    client-redir-response = client-Location *extension-field NL
    • CGI λŠ” Location ν•„λ“œ 값에 Client κ°€ λ¦¬λ‹€μ΄λ ‰νŠΈ ν•΄μ•Όν•  경둜λ₯Ό 적어쀀닀.
    • Server λŠ” 302 Found μƒνƒœ μ½”λ“œμ™€ ν•¨κ»˜ Location 헀더 ν•„λ“œλ₯Ό Client μ—κ²Œ μ „λ‹¬ν•˜λ©° Client κ°€ λ¦¬λ‹€μ΄λ ‰μ…˜μ„ μˆ˜ν–‰ν•  수 있게 ν•œλ‹€.
  3. Client Redirect with Document

    client-redirdoc-response = client-Location Status Content-Type
                                 *other-field NL response-body
    
    • CGI λŠ” Location ν•„λ“œ 값에 Client κ°€ λ¦¬λ‹€μ΄λ ‰νŠΈ ν•΄μ•Όν•  경둜λ₯Ό 적어주며, Content-Type μ—λŠ” λ°˜ν™˜ν•˜λŠ” λ¬Έμ„œμ˜ λ―Έλ””μ–΄ νƒ€μž…μ„ μ•Œλ €μ€€λ‹€.
    • Server λŠ” 302 Found μƒνƒœ μ½”λ“œμ™€ ν•¨κ»˜ Location 헀더 ν•„λ“œλ₯Ό Client μ—κ²Œ μ „λ‹¬ν•˜λ©° Client κ°€ λ¦¬λ‹€μ΄λ ‰μ…˜μ„ μˆ˜ν–‰ν•  수 있게 ν•œλ‹€.

5.2.1 Server 의 CGI 응닡 처리

  • CGI 의 응닡을 받은 Server λŠ” CGI κ°€ 보낸 header ν•„λ“œλ“€μ΄ μ˜λ―Έν•˜λŠ” λ°”κ°€ Server κ°€ μ„€μ •ν•˜λŠ” 응닡 헀더 ν•„λ“œκ°’κ³Ό μƒμΆ©λœλ‹€λ©΄ μ–΄λ–€ 값을 넣을지 κ²°μ •ν•΄μ•Όν•œλ‹€.
  • Server λŠ” CGI 의 응닡이 HTTP κ·œκ²©μ— λ§žλŠ”μ§€ μ κ²€ν•˜κ³  Client μ—κ²Œ μ „λ‹¬ν•΄μ•Όν•œλ‹€.

6 β€œμ„œλ²„λŠ” 죽지 μ•Šμ•„!”

  • Server λŠ” μ–΄λ–€ 상황에도 꺼지지 μ•Šμ•„μ•Όν•˜κ³ , HTTP/1.1 의 νŠΉμ„± 상 Connection keep-alive 의 경우 같은 Connection 객체가 μž¬μ‚¬μš©λ˜κΈ° λ•Œλ¬Έμ— μžμ› 정리가 맀우 μ€‘μš”ν•˜λ‹€.
  • μ΅œλŒ€ν•œ μžμ› 할당은 μƒμ„±μžμ—μ„œ, ν•΄μ œλŠ” μ†Œλ©Έμžμ—μ„œ μ²˜λ¦¬ν•œλ‹€(RAII).
  • ν•˜μ§€λ§Œ 생성과 μ†Œλ©Έ 사이클을 돌 수 μ—†κ³  반볡적으둜 μž¬μ‚¬μš©λ˜λŠ” 객체의 경우 μ•„λž˜μ˜ 기본적인 κ·œμΉ™λ“€μ„ μ£Όμ˜ν•˜μ—¬ μ§€μΌœμ•Όν•œλ‹€.
    • heap use after free/double free/pointer being freed was not allocated λ₯Ό ν”Όν•˜κΈ° μœ„ν•΄ ν• λ‹Ή ν•΄μ œ ν›„ 포인터 NULL 둜 μ„€μ •ν•œλ‹€.
    • ν•œμ •λœ fd ν…Œμ΄λΈ”μ΄ μž¬μ‚¬μš©λ˜κΈ° λ•Œλ¬Έμ— socket, file, pipe 의 I/O event μ‹œ μ‚¬μš©ν•˜λŠ” fd κ°€ μ „ν˜€ λ‹€λ₯Έ device λ₯Ό 가리킬 수 μžˆλ‹€. 이λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ μž¬μ‚¬μš© λ˜λŠ” fd λ³€μˆ˜λ“€μ€ close 이후 -1 둜 μ„€μ •ν•œλ‹€.

7 References

7.0 Socket 톡신

7.1 TCP Connection

7.2 I/O Multiplexing

7.3 SO_SNDLOWAT

7.4 HTTP

7.5 CGI