text: #!/usr/bin/env python3
  # coding: utf-8
  # Copyright 2016 Abram Hindle, https://github.com/tywtyw2002, and https://github.com/treedust
  # 
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  # 
  #     http://www.apache.org/licenses/LICENSE-2.0
  # 
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  # Do not use urllib's HTTP GET and POST mechanisms.
  # Write your own HTTP GET and POST
  # The point is to understand what you have to send and get experience with it
  
  import sys
  import socket
  import re
  # you may use urllib to encode data appropriately
  import urllib.parse
  
  bufferSize = 1024
  defaultHttpPort = 80
  
  def help():
      print("httpclient.py [GET/POST] [URL]\n")
  
  class HTTPResponse(object):
      def __init__(self, code=200, body=""):
          self.code = code
          self.body = body
  
  class HTTPClient(object):
      def connect(self, URL_components):
          """
          input:  a urlparsed dictionary
          return: the host that is connected to
          """
          # if there protocol scheme is given (not start with "http://")
          host = URL_components.hostname 
          if URL_components.hostname == None:
              # if the protocol scheme is not given 
              host = URL_components.path.split("/")[0]
          # if port is given
          port = URL_components.port
          if URL_components.port == None:
              # if port not given use default http port 80
              port = defaultHttpPort
          # catch any socket connection problem just in case
          try:
              self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
              self.socket.connect((host, port))
          except:
              print("Connection Error => Could not resolve host: {host} port: {port}".format(host=host,port=port))
              sys.exit(1)
          print("Successfully connect to: {host}:{port}...".format(host=host, port=port))
          return host
  
      def get_code(self, data):
          """
          input:  http response data
          return: response status code
          """
          begin = data.find("HTTP/")
          if begin != -1:
              return int(data[begin+9:12])
          else:
              return 500
          
      def get_first_line_of_header(self, data):
          """
          Not used in this assignment
          input:  http response data
          return: the first line of response header
          """
          begin = data.find("HTTP/")
          index = begin
          header = ""
          while True:
              if data[index] == "\r" or data[index] == "\n":
                  break
              header += data[index]
              index += 1
          return header
  
      def get_headers(self, data):
          """
          input:  http response data
          return: the response header
          """
          separatePoint = data.find("\r\n\r\n")
          if separatePoint == -1:
              separatePoint = data.find("\n\n")
          return data[0:separatePoint]
  
  
      def get_body(self, data):
          """
          input:  http response data
          return: the response body(content)
          """
          skipLength = 4
          separatePoint = data.find("\r\n\r\n")
          if separatePoint == -1:
              separatePoint = data.find("\n\n")
              skipLength = 2
          return data[separatePoint+skipLength:]
      
      def sendall(self, data):
          """
          input: data need to be sent through socket to the another end host
          send all data that is given
          """
          # catch any socket problem
          try:
              self.socket.sendall(data.encode('utf-8'))
          except:
              print("Socket Send Error => occur when try to send out data to destinated host.")
              sys.exit(1)
          
      def close(self):
          """
          close socket connection
          """
          self.socket.close()
  
      def recvall(self, sock):
          """
          input: socket 
          read everything from the socket
          """
          buffer = bytearray()
          done = False
          while not done:
              # catch any socket problem
              try:
                  part = sock.recv(1024)
              except:
                  print("Socket Recv Error => occur when try to send out data to destinated host.")
                  sys.exit(1)
              if (part):
                  buffer.extend(part)
              else:
                  done = not part
          return buffer
  
      def requestHeader(self, method, host, URL_components, contentType="text/plain", contentLength=0, userAgent="", connection="close", HttpVersion="HTTP/1.1", charset="utf-8"):
          """
          input:  all the necessary param for a GET request header (allow customize)
          return: generate a GET request header string
          """
          path = ""
          if host == URL_components.path.split("/")[0]:
              # no protocol scheme  eg: "www.google.com"
              length = 0
              for i in URL_components.path.split("/"):
                  if len(i) != 0 :
                      length += 1
              if length == 1:
                  # requested for root
                  path = "/"
              else: 
                  parts = URL_components.path.split("/")
                  path = URL_components.path[len(parts[0]):]
          else:
              if URL_components.path == "":
                  path = "/"
              else:
                  path = URL_components.path
          if userAgent == "":
              userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko)"
  
          return  "{method} {path} {HttpVersion}\r\n"\
                  "Host: {host}\r\n"\
                  "Accept-Charset: {charset}\r\n"\
                  "User-Agent: {userAgent}\r\n"\
                  "Content-Type: {contentType}; charset={charset}\r\n"\
                  "Content-Length: {contentLength}\r\n"\
                  "Connection: {connection}\r\n\r\n".format(method=method, path=path, HttpVersion=HttpVersion, host=host, charset=charset, userAgent=userAgent, contentType=contentType, contentLength=contentLength, connection=connection)
  
  
      def readResponse(self, socket):
          """
          input:  socket 
          process the data from socket
          """
          # recieve all data from the socket
          fullData = self.recvall(socket)
          # make a copy of the data
          fullDataOrigin = fullData
          try: 
              # try using UTF-8 to decode
              fullData = fullData.decode()
          except:
              # if fail, then look for the charset format to decode
              fullData = str(fullData)
              # look for the encode format in the response header
              charsetBegin = fullData.find("charset=")
              index = charsetBegin + 8
              charset = ""
              while True:
                  if fullData[index] == '\\':
                      break
                  charset += fullData[index]
                  index += 1
              # decode 
              fullData = fullDataOrigin.decode(charset)
          return fullData
      
      def convertFormData(self, formData):
          """
          input:  formData
          return: converted format in string
          The format => "VariableOne=ValueOne&VariableTwo=ValueTwo"
          """
          data = ""
          index = 0
          l = len(formData.keys()) - 1
          for key in formData.keys():
              data += str(key) + "=" + str(formData[key])
              if index != l:
                  data += "&"
              index += 1
          return data
  
      def GET(self, url, args=None):
          """
          Note: the GET request body should be empty
          input: url
          Handling GET request to given url
          """
          # url parsing
          URL_components = urllib.parse.urlparse(url)
          # TCP connection
          host = self.connect(URL_components)        
          # send GET request
          payload = self.requestHeader("GET" ,host, URL_components)
          self.sendall(payload)
          # read response
          fullData = self.readResponse(self.socket)
          # get code
          code = self.get_code(fullData)
          # get header
          # header = self.get_headers(fullData)
          # get body
          body = self.get_body(fullData)
          # close the connection before return
          self.close()
          print("---------------------------------HTTP Response---------------------------------")
          print("Response status code: "+str(code))
          print("Response body:\n"+body)
          print("-------------------------------------------------------------------------------")
          return HTTPResponse(code, body)
  
      def POST(self, url, args=None):
          """
          input: url and args as formData (formData should be in dictionary)
          Handling POST request to given url
          """
          # url parsing
          URL_components = urllib.parse.urlparse(url)
          # TCP connection
          host = self.connect(URL_components)  
          # send POST request
          contentLength = 0
          if args != None:
              # convert data into the format as the post body
              POSTbody = self.convertFormData(args)
              contentLength = len(POSTbody)
          payload = self.requestHeader("POST", host, URL_components, contentType="application/x-www-form-urlencoded", contentLength=contentLength)
          if args != None:
              payload += POSTbody
          # send 
          self.sendall(payload)
          # read Response
          fullData = self.readResponse(self.socket)
          # get code
          code = self.get_code(fullData)
          # get body
          body = self.get_body(fullData)
          # close the connection before return
          self.close()
          print("---------------------------------HTTP Response---------------------------------")
          print("Response status code: "+str(code))
          print("Response body:\n"+body)
          print("-------------------------------------------------------------------------------")
          return HTTPResponse(code, body)
  
      def command(self, url, command="GET", args=None):
          if (command == "POST"):
              return self.POST( url, args )
          else:
              return self.GET( url, args )
      
  if __name__ == "__main__":
      client = HTTPClient()
      if (len(sys.argv) <= 1):
          help()
          sys.exit(1)
      elif (len(sys.argv) == 3):
          print(client.command( sys.argv[2], sys.argv[1] ))
      else:
          print(client.command( sys.argv[1] ))
  