HTTP Server 실습

  • HTTP Server
  1. 브라우저를 통해 사용자가 HTTP Request Messsage를 보낸다.
  2. 서버는 그 Message를 해석하고 Request Message를 보낸다.
    2-1. 어느 페이지로 접속했는가?
    2-2. Query String은 어떤 페이지를 가지고 있는가?
    2-3. 특정 스크립트 요청이 있는가?
    2-4. 최종 응답을 어떤 방식으로 할 것인가?( HTML or 다운로드 )
  • HTTP Request만 하는 서버

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import http.server
    import socketserver

    PORT = 8000 # 서버에 접속하는 포트

    # 요청이 들어오면 어느 객체가 요청을 해석하고 처리할 것인가?
    # 문제에 대해 누가 처리할 것인가? = Handler
    Handler = http.server.SimpleHTTPRequestHandler

    with socketserver.TCPServer(("",PORT), Handler) as httpd:
    print("serving at PORT", PORT)
    httpd.serve_forever()
  • GET 방식을 통해 Query String 정보 받기

  • form 형식을 사용하여 POST Method 사용하기

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    from http.server import BaseHTTPRequestHandler, HTTPServer
    from urllib.parse import parse_qs, urlparse

    import requests
    from bs4 import BeautifulSoup
    import json

    PORT = 8000

    class Handler(BaseHTTPRequestHandler):

    # django는 한 페이지에서 접속 Method에 따라 기능을 분기
    # 회원 가입 페이지 domain.com/signup/
    # Get : 회원가입 양식 보여주기
    # Post : 전달받은 데이터를 처리해서 회원가입 진행하기(데이터베이스에 저장하기)

    def do_GET(self):
    self.send_response(200)
    self.send_header('Content-type', 'text/html')
    self.end_headers()

    # urlparse가 주소를 분석해주고 쿼리문을 가져옴
    query_text = urlparse(self.path).query

    print(query_text)
    # 딕셔너리 형태로 바꿔줌
    # 변수명은 하나지만 값이 여러개일수 있어서 value 값이 리스트 형태로 나옴
    query_vars = parse_qs(query_text)
    print(query_vars)
    message = "Welcome"
    form_html = """
    <form action='' method='post'>
    <label>Weight:<input type='text' name='weight'></label><br>
    <label>Height:<input type='text' name='height'></label><br>
    <input type='submit' value='Calc'>
    </form>
    """
    # query string으로 키와 몸무게를 전달받아서
    # bmi를 계산해서 message로 출력하시오.
    # 1. 딕셔너리를 다룰 수 있는가?
    # 2. 변수형에 대해 인지하고 있는가?
    # 3. 연산에 대해 알고 있는가?

    # 먼저 데이터가 있는지 없는지 항상 확인해야 한다.
    if 'weight' in query_vars and 'height' in query_vars:
    print('data')
    weight = float(query_vars['weight'][0])
    height = float(query_vars['height'][0])

    bmi = round(weight / (height/100)**2, 2)

    message += "BMI :"+str(bmi)

    message += form_html
    self.wfile.write(bytes(message, 'utf-8'))
    return

    def do_POST(self):
    content_length = int(self.headers.get('Content-Length'))
    post_body = self.rfile.read(content_length)
    queries = parse_qs(post_body.decode('utf-8'))
    print(queries)

    message = "POST Test<br>Result:"

    if 'weight' in queries and 'height' in queries:
    print('data')
    weight = float(queries['height'][0])
    height = float(queries['weight'][0])

    bmi = round(weight / (height/100)**2, 2)
    message += " BMI= "+str(bmi)

    self.send_response(200)
    self.send_header('Content-type', 'text/html')
    self.end_headers()

    self.wfile.write(bytes(message, 'utf-8'))
    return

    # method <-> with문으로 바꿀 수 있어야 함
    def run():
    server_address = ('127.0.0.1', PORT) # IP 주소를 직접 설정 가능
    httpd = HTTPServer(server_address, Handler)
    print("serving at PORT", PORT)
    httpd.serve_forever()

    run()
  • 최종 응답을 엑셀 파일로 저장하기 -> xlsxwriter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    # $ pip install xlsxwriter # 모듈 설치

    import http.server
    import socketserver
    import io

    import xlsxwriter

    from tempfile import NamedTemporaryFile

    PORT = 8000

    class Handler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):

    # flush 방식
    # io.BytesIO()가 시작되는 지점부터 화면에 출력되는 것들을 다 잡아줌
    output = io.BytesIO()
    # xlsxwriter로 만든 데이터를 BytesIO라는 데이터스트림으로 변환해서 출력
    # 실시간 데이터를 저장할 수 있음
    workbook = xlsxwriter.Workbook(output, {'in_memory':True})
    worksheet = workbook.add_worksheet()
    # openpyxl과 달리 셀번호가 0번부터 시작한다.
    worksheet.write(0,0, "EXCEL TEST")
    workbook.close()

    output.seek(0)

    self.send_response(200)
    self.send_header('Content-Disposition','attachment; filename=test.xlsx')
    self.send_header('Content-type','application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
    self.end_headers()
    self.wfile.write(output.read())
    return

    print("serving at port",PORT)
    httpd = socketserver.TCPServer(('',PORT),Handler)
    httpd.serve_forever()
  • 최종 응답을 엑셀 파일로 저장하기 -> openpyxl

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    import http.server
    import socketserver
    import io

    import xlsxwriter

    from tempfile import NamedTemporaryFile
    from openpyxl.workbook import Workbook
    from openpyxl.writer.excel import save_virtual_workbook

    PORT = 8000

    class Handler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):

    # openpyxl->
    wb = Workbook()
    ws = wb.active
    ws.cell(1,1,"openpyxl test 1")
    output = io.BytesIO(save_virtual_workbook(wb))
    # 파일 포인터는 항상 글의 끝에 위치하고 있다.
    # save할 때는 상관이 없으나, read할 때는 파일 포인터를 글의 처음으로 옮겨서 읽어야 한다.

    self.send_response(200)
    self.send_header('Content-Disposition','attachment; filename=test.xlsx')
    self.send_header('Content-type','application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
    self.end_headers()
    self.wfile.write(output.read())
    return

    print("serving at port",PORT)
    httpd = socketserver.TCPServer(('',PORT),Handler)
    httpd.serve_forever()
  • CGI

    • Common Gateway Interface
    • CGI 장점 : 특별한 추가 프로그램 없이도 여러 언어의 스크립트 실행 가능
    • CGI 단점 : 요청이 있을 때마다 프로세스(응용프로그램)를 새로 실행(time overhead)
  • CGI Test

    • 127.0.0.1/cgi/test.py를 통해 실행 결과를 확인한다.
    • 127.0.0.1/cgi/lotto.py를 통해 실행 결과를 확인한다.
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      # 테스트 파일 - test.py
      # 프로젝트 내에서 CGI 폴더를 생성
      # CGI 폴더 안에 test.py 생성

      # which python을 통해 경로 지정
      #!/home/jinwook/server_basic/venv/bin/python
      print("COntent-type: text/html\n")
      print("<html><head><title>CGI테스트</title></head><body>CGI Server Testing</body></html>")

      # 윈도우와는 달리 리눅스는 실행 권한을 주어야 한다.
      # 스크립트에 실행권한 주기 : chmod ugo+x cgi/test.py



      # 테스트파일 - lotto.py

      #!/home/jinwook/server_basic/venv/bin/python
      import random

      # 로또 예상번호 5게임 출력하기

      # 로또는 1게임당 1~45 사이의 정수 중 6개를 중복없이 뽑아야 한다.

      # 1. 랜덤하게 숫자 뽑아서 채우기
      # 1-1. list 방식
      # random.randint(1,45) <- 끝에 숫자를 포함한다.
      numbers = []
      while len(numbers) < 6:
      number = random.randint(1,45)
      if number not in numbers:
      numbers.append(number)

      # 1-1. set 방식
      numbers = set()
      while len(numbers) < 6:
      number = random.randint(1,45)
      numbers.add(number)

      # 2. 원본을 만들어 두고 랜덤하게 몇 개 뽑는 방법
      # 2-1. 원본은 순서대로, 뽑는 것은 랜덤으로
      original_numbers = [x for x in range(1,46)]

      random.sample(original_numbers, 6)

      # 2-2. 원본을 랜덤으로 만들고, 뽑은 것은 순서대로
      random.shuffle(original_numbers)

      numbers = original_numbers[:6]
      numbers = original_numbers[-6:]


      print("COntent-type: text/html\n")
      print("<html><head><title>CGI테스트</title></head><body>"+str(numbers)+"</body></html>")



      # cgi.py
      # HTTP Server 관련 소스

      import http.server

      PORT = 8000

      class Handler(http.server.CGIHTTPRequestHandler):
      # 특정 폴더에 있는 파일들만 엑세스하도록 지정,
      # 지정하지 않으면 모든 파일을 엑세스하게 됨
      cgi_directories = ['/cgi']

      with http.server.HTTPServer(("",PORT), Handler) as httpd:
      print("serving at port", PORT)
      httpd.serve_forever()
Share