Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

sync.py 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. # -*- coding: utf-8 -
  2. #
  3. # This file is part of gunicorn released under the MIT license.
  4. # See the NOTICE for more information.
  5. #
  6. from datetime import datetime
  7. import errno
  8. import os
  9. import select
  10. import socket
  11. import ssl
  12. import sys
  13. import gunicorn.http as http
  14. import gunicorn.http.wsgi as wsgi
  15. import gunicorn.util as util
  16. import gunicorn.workers.base as base
  17. from gunicorn import six
  18. class StopWaiting(Exception):
  19. """ exception raised to stop waiting for a connnection """
  20. class SyncWorker(base.Worker):
  21. def accept(self, listener):
  22. client, addr = listener.accept()
  23. client.setblocking(1)
  24. util.close_on_exec(client)
  25. self.handle(listener, client, addr)
  26. def wait(self, timeout):
  27. try:
  28. self.notify()
  29. ret = select.select(self.wait_fds, [], [], timeout)
  30. if ret[0]:
  31. if self.PIPE[0] in ret[0]:
  32. os.read(self.PIPE[0], 1)
  33. return ret[0]
  34. except select.error as e:
  35. if e.args[0] == errno.EINTR:
  36. return self.sockets
  37. if e.args[0] == errno.EBADF:
  38. if self.nr < 0:
  39. return self.sockets
  40. else:
  41. raise StopWaiting
  42. raise
  43. def is_parent_alive(self):
  44. # If our parent changed then we shut down.
  45. if self.ppid != os.getppid():
  46. self.log.info("Parent changed, shutting down: %s", self)
  47. return False
  48. return True
  49. def run_for_one(self, timeout):
  50. listener = self.sockets[0]
  51. while self.alive:
  52. self.notify()
  53. # Accept a connection. If we get an error telling us
  54. # that no connection is waiting we fall down to the
  55. # select which is where we'll wait for a bit for new
  56. # workers to come give us some love.
  57. try:
  58. self.accept(listener)
  59. # Keep processing clients until no one is waiting. This
  60. # prevents the need to select() for every client that we
  61. # process.
  62. continue
  63. except EnvironmentError as e:
  64. if e.errno not in (errno.EAGAIN, errno.ECONNABORTED,
  65. errno.EWOULDBLOCK):
  66. raise
  67. if not self.is_parent_alive():
  68. return
  69. try:
  70. self.wait(timeout)
  71. except StopWaiting:
  72. return
  73. def run_for_multiple(self, timeout):
  74. while self.alive:
  75. self.notify()
  76. try:
  77. ready = self.wait(timeout)
  78. except StopWaiting:
  79. return
  80. if ready is not None:
  81. for listener in ready:
  82. if listener == self.PIPE[0]:
  83. continue
  84. try:
  85. self.accept(listener)
  86. except EnvironmentError as e:
  87. if e.errno not in (errno.EAGAIN, errno.ECONNABORTED,
  88. errno.EWOULDBLOCK):
  89. raise
  90. if not self.is_parent_alive():
  91. return
  92. def run(self):
  93. # if no timeout is given the worker will never wait and will
  94. # use the CPU for nothing. This minimal timeout prevent it.
  95. timeout = self.timeout or 0.5
  96. # self.socket appears to lose its blocking status after
  97. # we fork in the arbiter. Reset it here.
  98. for s in self.sockets:
  99. s.setblocking(0)
  100. if len(self.sockets) > 1:
  101. self.run_for_multiple(timeout)
  102. else:
  103. self.run_for_one(timeout)
  104. def handle(self, listener, client, addr):
  105. req = None
  106. try:
  107. if self.cfg.is_ssl:
  108. client = ssl.wrap_socket(client, server_side=True,
  109. **self.cfg.ssl_options)
  110. parser = http.RequestParser(self.cfg, client)
  111. req = six.next(parser)
  112. self.handle_request(listener, req, client, addr)
  113. except http.errors.NoMoreData as e:
  114. self.log.debug("Ignored premature client disconnection. %s", e)
  115. except StopIteration as e:
  116. self.log.debug("Closing connection. %s", e)
  117. except ssl.SSLError as e:
  118. if e.args[0] == ssl.SSL_ERROR_EOF:
  119. self.log.debug("ssl connection closed")
  120. client.close()
  121. else:
  122. self.log.debug("Error processing SSL request.")
  123. self.handle_error(req, client, addr, e)
  124. except EnvironmentError as e:
  125. if e.errno not in (errno.EPIPE, errno.ECONNRESET):
  126. self.log.exception("Socket error processing request.")
  127. else:
  128. if e.errno == errno.ECONNRESET:
  129. self.log.debug("Ignoring connection reset")
  130. else:
  131. self.log.debug("Ignoring EPIPE")
  132. except Exception as e:
  133. self.handle_error(req, client, addr, e)
  134. finally:
  135. util.close(client)
  136. def handle_request(self, listener, req, client, addr):
  137. environ = {}
  138. resp = None
  139. try:
  140. self.cfg.pre_request(self, req)
  141. request_start = datetime.now()
  142. resp, environ = wsgi.create(req, client, addr,
  143. listener.getsockname(), self.cfg)
  144. # Force the connection closed until someone shows
  145. # a buffering proxy that supports Keep-Alive to
  146. # the backend.
  147. resp.force_close()
  148. self.nr += 1
  149. if self.nr >= self.max_requests:
  150. self.log.info("Autorestarting worker after current request.")
  151. self.alive = False
  152. respiter = self.wsgi(environ, resp.start_response)
  153. try:
  154. if isinstance(respiter, environ['wsgi.file_wrapper']):
  155. resp.write_file(respiter)
  156. else:
  157. for item in respiter:
  158. resp.write(item)
  159. resp.close()
  160. request_time = datetime.now() - request_start
  161. self.log.access(resp, req, environ, request_time)
  162. finally:
  163. if hasattr(respiter, "close"):
  164. respiter.close()
  165. except EnvironmentError:
  166. # pass to next try-except level
  167. six.reraise(*sys.exc_info())
  168. except Exception:
  169. if resp and resp.headers_sent:
  170. # If the requests have already been sent, we should close the
  171. # connection to indicate the error.
  172. self.log.exception("Error handling request")
  173. try:
  174. client.shutdown(socket.SHUT_RDWR)
  175. client.close()
  176. except EnvironmentError:
  177. pass
  178. raise StopIteration()
  179. raise
  180. finally:
  181. try:
  182. self.cfg.post_request(self, req, environ, resp)
  183. except Exception:
  184. self.log.exception("Exception in post_request hook")