http_client.py 5.9 KB

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