Advertisement
Guest User

Untitled

a guest
Mar 30th, 2020
136
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.42 KB | None | 0 0
  1. """ //////////////////////////////////////////////////
  2. HTTP PROXY IMPLEMENTATION
  3. ////////////////////////////////////////////////// """
  4.  
  5. """ ////////////////////
  6. NOTES
  7. //////////////////// """
  8.  
  9. """ Build Using Pycharm """
  10. """ ///// CODE SKELETON IS PROVIDED BY COMPUTER NETWORKS COURSE TEACHING ASSISTANTS ///// """
  11.  
  12. """ TOTAL USED FUNCTIONS = ... """
  13. """ TOTAL UNUSED FUNCTIONS = ... """
  14.  
  15. """ ///////////////////////////////////////////////////////////////////////////////////////////// """
  16. """ ///////////////////////////////////////////////////////////////////////////////////////////// """
  17.  
  18. """ IMPORTS & GLOBAL VARIABLES """
  19. import sys
  20. import os
  21. import enum
  22. import re
  23.  
  24. client_addr = ("127.0.0.1", 9877)
  25.  
  26.  
  27. class HttpRequestInfo(object):
  28. """
  29. Represents a HTTP request information
  30.  
  31. Since you'll need to standardize all requests you get
  32. as specified by the document, after you parse the
  33. request from the TCP packet put the information you
  34. get in this object.
  35.  
  36. To send the request to the remote server, call to_http_string
  37. on this object, convert that string to bytes then send it in
  38. the socket.
  39.  
  40. client_address_info: address of the client;
  41. the client of the proxy, which sent the HTTP request.
  42.  
  43. requested_host: the requested website, the remote website
  44. we want to visit.
  45.  
  46. requested_port: port of the webserver we want to visit.
  47.  
  48. requested_path: path of the requested resource, without
  49. including the website name.
  50.  
  51. NOTE: you need to implement to_http_string() for this class.
  52. """
  53.  
  54. def __init__(self, client_info, method: str, requested_host: str,
  55. requested_port: int,
  56. requested_path: str,
  57. headers: list):
  58. self.method = method
  59. self.client_address_info = client_info
  60. self.requested_host = requested_host
  61. self.requested_port = requested_port
  62. self.requested_path = requested_path
  63. # Headers will be represented as a list of lists
  64. # for example ["Host", "www.google.com"]
  65. # if you get a header as:
  66. # "Host: www.google.com:80"
  67. # convert it to ["Host", "www.google.com"] note that the
  68. # port is removed (because it goes into the request_port variable)
  69. self.headers = headers
  70.  
  71. def to_http_string(self):
  72. """
  73. Convert the HTTP request/response
  74. to a valid HTTP string.
  75. As the protocol specifies:
  76. [request_line]\r\n
  77. [header]\r\n
  78. [headers..]\r\n
  79. \r\n
  80. You still need to convert this string
  81. to byte array before sending it to the socket,
  82. keeping it as a string in this stage is to ease
  83. debugging and testing.
  84. """
  85. HttpString = '';
  86. HttpString += self.method + ' '
  87. if self.requested_path == '':
  88. HttpString += self.requested_path
  89. else:
  90. HttpString += self.requested_path
  91. HttpString += ' ' + 'HTTP/1.0' + '\r\n'
  92. for item in self.headers:
  93. HttpString += item[0] + ': ' + item[1] + '\r\n'
  94. HttpString += '\r\n'
  95. return HttpString
  96.  
  97. def to_byte_array(self, http_string):
  98. """
  99. Converts an HTTP string to a byte array.
  100. """
  101. return bytes(http_string, "UTF-8")
  102.  
  103. def display(self):
  104. print(f"Client:", self.client_address_info)
  105. print(f"Method:", self.method)
  106. print(f"Host:", self.requested_host)
  107. print(f"Port:", self.requested_port)
  108. stringified = [": ".join([k, v]) for (k, v) in self.headers]
  109. print("Headers:\n", "\n".join(stringified))
  110.  
  111.  
  112. class HttpErrorResponse(object):
  113. """
  114. Represents a proxy-error-response.
  115. """
  116.  
  117. def __init__(self, code, message):
  118. self.code = code
  119. self.message = message
  120.  
  121. def to_http_string(self):
  122. """ Same as above """
  123. pass
  124.  
  125. def to_byte_array(self, http_string):
  126. """
  127. Converts an HTTP string to a byte array.
  128. """
  129. return bytes(http_string, "UTF-8")
  130.  
  131. def display(self):
  132. print(self.to_http_string())
  133.  
  134.  
  135. class HttpRequestState(enum.Enum):
  136. """
  137. The values here have nothing to do with
  138. response values i.e. 400, 502, ..etc.
  139.  
  140. Leave this as is, feel free to add yours.
  141. """
  142. INVALID_INPUT = 0
  143. NOT_SUPPORTED = 1
  144. GOOD = 2
  145. PLACEHOLDER = -1
  146.  
  147.  
  148. def entry_point(proxy_port_number):
  149. """
  150. Entry point, start your code here.
  151.  
  152. Please don't delete this function,
  153. but feel free to modify the code
  154. inside it.
  155. """
  156.  
  157. setup_sockets(proxy_port_number)
  158. print("*" * 50)
  159. print("[entry_point] Implement me!")
  160. print("*" * 50)
  161. return None
  162.  
  163.  
  164. def setup_sockets(proxy_port_number):
  165. """
  166. Socket logic MUST NOT be written in the any
  167. class. Classes know nothing about the sockets.
  168.  
  169. But feel free to add your own classes/functions.
  170.  
  171. Feel free to delete this function.
  172. """
  173. print("Starting HTTP proxy on port:", proxy_port_number)
  174.  
  175. # when calling socket.listen() pass a number
  176. # that's larger than 10 to avoid rejecting
  177. # connections automatically.
  178. print("*" * 50)
  179. print("[setup_sockets] Implement me!")
  180. print("*" * 50)
  181. return None
  182.  
  183.  
  184. def do_socket_logic():
  185. """
  186. Example function for some helper logic, in case you
  187. want to be tidy and avoid stuffing the main function.
  188.  
  189. Feel free to delete this function.
  190. """
  191. pass
  192.  
  193.  
  194. def http_request_pipeline(source_addr, http_raw_data):
  195. """
  196. HTTP request processing pipeline.
  197.  
  198. - Validates the given HTTP request and returns
  199. an error if an invalid request was given.
  200. - Parses it
  201. - Returns a sanitized HttpRequestInfo
  202.  
  203. returns:
  204. HttpRequestInfo if the request was parsed correctly.
  205. HttpErrorResponse if the request was invalid.
  206.  
  207. Please don't remove this function, but feel
  208. free to change its content
  209. """
  210. # Parse HTTP request
  211. validity = check_http_request_validity(http_raw_data)
  212. # Return error if needed, then:
  213. # parse_http_request()
  214. # sanitize_http_request()
  215. # Validate, sanitize, return Http object.
  216. print("*" * 50)
  217. print("[http_request_pipeline] Implement me!")
  218. print("*" * 50)
  219. return None
  220.  
  221.  
  222. def parse_http_request(source_addr, http_raw_data):
  223. print("*" * 30)
  224. print("Parsing Your Input")
  225. print("*" * 30)
  226. http_raw_data = http_raw_data.replace('\r\n\r\n', '')
  227. In = http_raw_data.split('\r\n')
  228.  
  229. # if len(In) == 1:
  230. # In = http_raw_data.split('\r\n')
  231.  
  232. Method = In[0].split(' ')[0]
  233. Path = In[0].split(' ')[1]
  234. HTTP_Version = In[0].split(' ')[2]
  235.  
  236. if Path == "/" and In[1] == '':
  237. print("Invalid Input")
  238. elif Path[0] == "/" and In[1] == '':
  239. hostname = In[0].split(' ')[0]
  240. header_value = In[0].split(' ')[1]
  241. print("Relative Path")
  242. print("Method: " + Method)
  243. print("Path: " + Path)
  244. print("HTTP Version: " + HTTP_Version)
  245. print("Header: " + hostname.replace(':', ''))
  246. print("Header Name: " + header_value)
  247. HeadersCustom = check_extra_header(http_raw_data)
  248. HeadersCustom.append([hostname.replace(':', ''), header_value])
  249. ret = HttpRequestInfo(source_addr, Method, header_value, 80, Path, HeadersCustom)
  250. return ret
  251. pass
  252. elif Path[0] == "/" and In[1] != '':
  253. hostname = In[1].split(' ')[0]
  254. header_value = In[1].split(' ')[1]
  255. print("Relative Path")
  256. print("Method: " + Method)
  257. print("Path: " + Path)
  258. print("HTTP Version: " + HTTP_Version)
  259. print("Header: " + hostname.replace(':', ''))
  260. print("Header Name: " + header_value)
  261. HeadersCustom = check_extra_header(http_raw_data)
  262. HeadersCustom.append([hostname.replace(':', ''), header_value])
  263. ret = HttpRequestInfo(source_addr, Method, header_value, 80, Path, HeadersCustom)
  264. return ret
  265. pass
  266. else:
  267. print("Normal Path")
  268. print("Method: " + Method)
  269. print("Path: " + Path)
  270. print("HTTP Version: " + HTTP_Version)
  271. HeadersCustom = check_extra_header(http_raw_data)
  272. HeadersCustom.append(['Host:', Path])
  273. ret = HttpRequestInfo(source_addr, Method, Path, 80, Path, HeadersCustom)
  274. return ret
  275.  
  276.  
  277. pass
  278.  
  279.  
  280. def check_http_request_validity(http_raw_data) -> HttpRequestState:
  281. print("*" * 30)
  282. print("Validating Your Input")
  283. print("*" * 30)
  284.  
  285. http_raw_data = http_raw_data.replace('\r\n\r\n', '')
  286. command = http_raw_data.split('\r\n')
  287. z = (len(command))
  288.  
  289. # if len(command) == 1:
  290. # command = http_raw_data.split('\r\n')
  291.  
  292. print(z)
  293. rex = re.compile("[a-z]{3} [\a-z]{1,} [\a-z]{3,}")
  294. rex2 = re.compile("[\a-z]{4,}: [\a-z]{3,}")
  295. meth = command[0].split(' ')[0]
  296. host_test = command[0].split(' ')[1]
  297. if rex.match(command[0].lower()) and method_checker(meth) == 1 and z < 2:
  298. print("Valid format")
  299. x = command[0].split(' ')[0]
  300. y = command[0].split(' ')[1]
  301. if y == "/":
  302. print("Invalid Input")
  303. return HttpRequestState.INVALID_INPUT
  304. else:
  305. parse_http_request(client_addr, http_raw_data)
  306. return HttpRequestState.GOOD
  307. pass
  308. elif rex.match(command[0].lower()) and method_checker(meth) == 1:
  309. print("Valid format")
  310. parse_http_request(client_addr, http_raw_data)
  311. return HttpRequestState.GOOD
  312. pass
  313. elif rex.match(command[0].lower()) and rex2.match(command[1].lower()) and method_checker(meth) == 1:
  314. print("Valid format")
  315. parse_http_request(client_addr, http_raw_data)
  316. return HttpRequestState.GOOD
  317. pass
  318. else:
  319. print("Invalid format")
  320. if len(command) == 1 and host_test.startswith('/'):
  321. return HttpRequestState.INVALID_INPUT
  322. elif method_checker(meth) == 0 and command[1].find(":") == -1:
  323. return HttpRequestState.INVALID_INPUT
  324. elif method_checker(meth) == 0 and command[0].find("/1.0") == -1:
  325. return HttpRequestState.INVALID_INPUT
  326. if method_checker(meth) == 0:
  327. return HttpRequestState.NOT_SUPPORTED
  328. elif method_checker(meth) == 2:
  329. return HttpRequestState.INVALID_INPUT
  330. pass
  331.  
  332. pass
  333.  
  334.  
  335. def sanitize_http_request(request_info: HttpRequestInfo):
  336. """
  337. Puts an HTTP request on the sanitized (standard form)
  338.  
  339. returns:
  340. A modified object of the HttpRequestInfo with
  341. sanitized fields
  342.  
  343. for example, expand a URL to relative path + Host header.
  344. """
  345. print("*" * 50)
  346. print("[sanitize_http_request] Implement me!")
  347. print("*" * 50)
  348. ret = HttpRequestInfo(None, None, None, None, None, None)
  349. return ret
  350.  
  351.  
  352. #######################################
  353. # Leave the code below as is.
  354. #######################################
  355.  
  356.  
  357. def get_arg(param_index, default=None):
  358. """
  359. Gets a command line argument by index (note: index starts from 1)
  360. If the argument is not supplies, it tries to use a default value.
  361.  
  362. If a default value isn't supplied, an error message is printed
  363. and terminates the program.
  364. """
  365. try:
  366. return sys.argv[param_index]
  367. except IndexError as e:
  368. if default:
  369. return default
  370. else:
  371. print(e)
  372. print(
  373. f"[FATAL] The comand-line argument #[{param_index}] is missing")
  374. exit(-1) # Program execution failed.
  375.  
  376.  
  377. def check_file_name():
  378. """
  379. Checks if this file has a valid name for *submission*
  380.  
  381. leave this function and as and don't use it. it's just
  382. to notify you if you're submitting a file with a correct
  383. name.
  384. """
  385. script_name = os.path.basename(__file__)
  386. import re
  387. matches = re.findall(r"(\d{4}_){,2}lab2\.py", script_name)
  388. if not matches:
  389. print(f"[WARN] File name is invalid [{script_name}]")
  390. else:
  391. print(f"[LOG] File name is correct.")
  392.  
  393.  
  394. def check_extra_header(command):
  395. command = command.replace('\r\n\r\n','')
  396. Part = command.split('\r\n')
  397. length = len(Part)
  398. HeadersCustom = []
  399. # if length == 1:
  400. # Part = command.split('\r\n')
  401. i = 2
  402. rex = re.compile("[\a-z]{2,}: [\a-z]{3,}")
  403. while i < length:
  404. if rex.match(Part[i].lower()):
  405. Header = Part[i].split(' ')[0]
  406. Header_Name = Part[i].split(' ')[1]
  407. New = [Header.replace(':', ''), Header_Name]
  408. print(New)
  409. HeadersCustom.append(New)
  410. i += 1
  411.  
  412.  
  413. return HeadersCustom
  414.  
  415.  
  416.  
  417. def method_checker(method):
  418. if method == "GET":
  419. return 1
  420. elif method == "DELETE" or method == "PUT" or method == "POST" or method == "HEAD":
  421. print("Error 501 Not Implemented Request")
  422. return 0
  423. else:
  424. print("Error 400 Bad Request")
  425. return 2
  426.  
  427.  
  428. pass
  429.  
  430.  
  431. def main():
  432. print("*" * 50)
  433. print(f"[LOG] Printing command line arguments [{', '.join(sys.argv)}]")
  434. check_file_name()
  435. print("*" * 50)
  436. print("\n")
  437. command = input("")
  438. check_http_request_validity(command)
  439.  
  440.  
  441. # This argument is optional, defaults to 18888
  442. # proxy_port_number = get_arg(1, 18888)
  443. # entry_point(proxy_port_number)
  444.  
  445.  
  446. if __name__ == "__main__":
  447. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement