http_client.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. # this code based on Daniel Krech's RDFLib HTTP client code (see rdflib.net)
  2. import sys
  3. import supervisor.medusa.text_socket as socket
  4. from supervisor.compat import print_function
  5. from supervisor.compat import urlparse
  6. from supervisor.compat import as_bytes
  7. from supervisor.compat import as_string
  8. from supervisor.compat import encodestring
  9. from supervisor.medusa import asynchat_25 as asynchat
  10. CR="\x0d"
  11. LF="\x0a"
  12. CRLF=CR+LF
  13. class Listener(object):
  14. def status(self, url, status):
  15. pass
  16. def error(self, url, error):
  17. print_function(url, error)
  18. def response_header(self, url, name, value):
  19. pass
  20. def done(self, url):
  21. pass
  22. #noinspection PyUnusedLocal
  23. def feed(self, url, data):
  24. sys.stdout.write(data)
  25. sys.stdout.flush()
  26. def close(self, url):
  27. pass
  28. class HTTPHandler(asynchat.async_chat):
  29. def __init__(self, listener, username='', password=None):
  30. asynchat.async_chat.__init__(self)
  31. self.listener = listener
  32. self.user_agent = 'Supervisor HTTP Client'
  33. self.buffer = ''
  34. self.set_terminator(CRLF)
  35. self.connected = 0
  36. self.part = self.status_line
  37. self.chunk_size = 0
  38. self.chunk_read = 0
  39. self.length_read = 0
  40. self.length = 0
  41. self.encoding = None
  42. self.username = username
  43. self.password = password
  44. self.url = None
  45. self.error_handled = False
  46. def get(self, serverurl, path=''):
  47. if self.url is not None:
  48. raise AssertionError('Already doing a get')
  49. self.url = serverurl + path
  50. scheme, host, path_ignored, params, query, fragment = urlparse.urlparse(
  51. self.url)
  52. if not scheme in ("http", "unix"):
  53. raise NotImplementedError
  54. self.host = host
  55. if ":" in host:
  56. hostname, port = host.split(":", 1)
  57. port = int(port)
  58. else:
  59. hostname = host
  60. port = 80
  61. self.path = path
  62. self.port = port
  63. if scheme == "http":
  64. ip = hostname
  65. self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
  66. self.connect((ip, self.port))
  67. elif scheme == "unix":
  68. socketname = serverurl[7:]
  69. self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM)
  70. self.connect(socketname)
  71. def close (self):
  72. self.listener.close(self.url)
  73. self.connected = 0
  74. self.del_channel()
  75. self.socket.close()
  76. self.url = "CLOSED"
  77. def header(self, name, value):
  78. self.push('%s: %s' % (name, value))
  79. self.push(CRLF)
  80. def handle_error (self):
  81. if self.error_handled:
  82. return
  83. if 1 or self.connected:
  84. t,v,tb = sys.exc_info()
  85. msg = 'Cannot connect, error: %s (%s)' % (t, v)
  86. self.listener.error(self.url, msg)
  87. self.part = self.ignore
  88. self.close()
  89. self.error_handled = True
  90. del t
  91. del v
  92. del tb
  93. def handle_connect(self):
  94. self.connected = 1
  95. method = "GET"
  96. version = "HTTP/1.1"
  97. self.push("%s %s %s" % (method, self.path, version))
  98. self.push(CRLF)
  99. self.header("Host", self.host)
  100. self.header('Accept-Encoding', 'chunked')
  101. self.header('Accept', '*/*')
  102. self.header('User-agent', self.user_agent)
  103. if self.password:
  104. auth = '%s:%s' % (self.username, self.password)
  105. auth = as_string(encodestring(as_bytes(auth))).strip()
  106. self.header('Authorization', 'Basic %s' % auth)
  107. self.push(CRLF)
  108. self.push(CRLF)
  109. def feed(self, data):
  110. self.listener.feed(self.url, data)
  111. def collect_incoming_data(self, bytes):
  112. self.buffer = self.buffer + bytes
  113. if self.part==self.body:
  114. self.feed(self.buffer)
  115. self.buffer = ''
  116. def found_terminator(self):
  117. self.part()
  118. self.buffer = ''
  119. def ignore(self):
  120. self.buffer = ''
  121. def status_line(self):
  122. line = self.buffer
  123. version, status, reason = line.split(None, 2)
  124. status = int(status)
  125. if not version.startswith('HTTP/'):
  126. raise ValueError(line)
  127. self.listener.status(self.url, status)
  128. if status == 200:
  129. self.part = self.headers
  130. else:
  131. self.part = self.ignore
  132. msg = 'Cannot read, status code %s' % status
  133. self.listener.error(self.url, msg)
  134. self.close()
  135. return version, status, reason
  136. def headers(self):
  137. line = self.buffer
  138. if not line:
  139. if self.encoding=="chunked":
  140. self.part = self.chunked_size
  141. else:
  142. self.part = self.body
  143. self.set_terminator(self.length)
  144. else:
  145. name, value = line.split(":", 1)
  146. if name and value:
  147. name = name.lower()
  148. value = value.strip()
  149. if name=="Transfer-Encoding".lower():
  150. self.encoding = value
  151. elif name=="Content-Length".lower():
  152. self.length = int(value)
  153. self.response_header(name, value)
  154. def response_header(self, name, value):
  155. self.listener.response_header(self.url, name, value)
  156. def body(self):
  157. self.done()
  158. self.close()
  159. def done(self):
  160. self.listener.done(self.url)
  161. def chunked_size(self):
  162. line = self.buffer
  163. if not line:
  164. return
  165. chunk_size = int(line.split()[0], 16)
  166. if chunk_size==0:
  167. self.part = self.trailer
  168. else:
  169. self.set_terminator(chunk_size)
  170. self.part = self.chunked_body
  171. self.length += chunk_size
  172. def chunked_body(self):
  173. line = self.buffer
  174. self.set_terminator(CRLF)
  175. self.part = self.chunked_size
  176. self.feed(line)
  177. def trailer(self):
  178. # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
  179. # trailer = *(entity-header CRLF)
  180. line = self.buffer
  181. if line==CRLF:
  182. self.done()
  183. self.close()