2015년 12월 29일 화요일

C 언어 - 네트워크 프로그래밍 (HTTP 서버 예제)

HTTP 서버 예제


설명할 예제


github: https://github.com/bjtj/tjsamples/tree/master/linux/network/simplehttpserver
  • simple-http-server.c
  • http-parser.c
  • http-parser.h
  • single-thread-http-server.c

clone 받기

$ git clone https://github.com/bjtj/tjsamples

HTTP 프로토콜


HTTP 패킷 구조


HTTP 패킷은 요청(request) 와 응답(response) 로 구분되며 요청 및 응답이 동일한 구조를 갖는다.


  • HTTP header
  • HTTP content (content 는 없을 수도 있음)


HTTP Header 구조


First line\r\n
Header field\r\n
...
\r\n
\r\n 로 줄이 구분된다.
줄은 각 의미 단위이다.
Header field 는 이름/값 형태의 묶음으로 여러 개로 확장 가능하다.
header 의 끝은 \r\n 을 하나 더 추가해 표시한다.

예) 요청

GET / HTTP/1.1\r\n
User-Agent: curl/7.35.0\r\n
Host: example.com\r\n
Accept: */*\r\n
\r\n

예) 응답

HTTP/1.1 200 OK\r\n
Accept-Ranges: bytes\r\n
Cache-Control: max-age=604800\r\n
Content-Type: text/html\r\n
Date: Wed, 30 Dec 2015 02:17:52 GMT\r\n
Etag: "359670651+gzip"\r\n
Expires: Wed, 06 Jan 2016 02:17:52 GMT\r\n
Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT\r\n
Server ECS (cpm/F9D5) is not blacklisted\r\n
Server: ECS (cpm/F9D5)\r\n
Vary: Accept-Encoding\r\n
X-Cache: HIT\r\n
x-ec-custom-error: 1\r\n
Content-Length: 1270\r\n
\r\n
<!doctype html>
<html>
<head>
    <title>Example Domain</title>
    <meta charset="utf-8" />
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style type="text/css">
    body {
single-thread-http-server
(생략)

First line 구조

<의미1> <의미2> <의미3>\r\n
3가지 의미가 공백으로 구분되어 있다.
요청과 응답에 따라 의미가 다르다.

요청

  • 의미1 : method
  • 의미2 : uri
  • 의미3 : HTTP 프로토콜 버전


응답

  • 의미1 : HTTP 프로토콜 버전
  • 의미2 : 숫자 형태의 응답 상태 값
  • 의미3 : 문자열 형태의 응답 상태 (공백이 포함될 수 있음)


Header field 구조


<이름>: <값>\r\n
예를 들어 Content-Length: 0\r\n 식으로 HTTP content 에 대한 정보 및 여러 통신에 필요한 부가 정보를 header field 로 표현한다.

표준 header fields

참고: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
  • Content-Type : HTTP content 의 형태 (예: 일반 문서는 text/plain, html 문서는 text/html 등으로 표현)
  • Content-Length : HTTP content 의 크기 (byte 단위)


HTTP 서버 구현


아래 예제들은 linux 환경에서 구현됨

간단한 예제로 시작




소스 내용은 어떠한 요청이든 아래 응답을 보내고 종료하는 HTTP 서버이다.

HTTP/1.1 200 OK\r\n
Content-Length: 5\r\n
Content-Type: text/plain\r\n
\r\n
Hello

위 상태로는 request 에 적절히 대응하기 어려운 면이 있다.

수정 필요한 부분


  • 여러 요청 처리
  • 현재 구현은 recv 한번으로 HTTP header 를 모두 받도록 되어 있지만 TCP 통신 특성 상 데이터가 파편화 되어 수신 될 수 있음
  • HTTP header 를 먼저 받아 content 처리 방법이 선택되어야 한다.


수정한 소스




while (1) 으로 무한 루프 돌면서 accept() s_handle_client() 에서 요청 처리


s_handle_client() 에서 1바이트씩 읽어가며 header 부분을 읽도록 수정


HTTP header parse 는 덤으로 구현함


댓글 2개: