autobahn 0.3.1__zip

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. Python27/Lib/site-packages/autobahn/__init__.py +22 -0
  2. Python27/Lib/site-packages/autobahn/__init__.pyc +0 -0
  3. Python27/Lib/site-packages/autobahn/autobahn.py +76 -0
  4. Python27/Lib/site-packages/autobahn/autobahn.pyc +0 -0
  5. Python27/Lib/site-packages/autobahn/case/__init__.py +207 -0
  6. Python27/Lib/site-packages/autobahn/case/__init__.pyc +0 -0
  7. Python27/Lib/site-packages/autobahn/case/case.py +68 -0
  8. Python27/Lib/site-packages/autobahn/case/case.pyc +0 -0
  9. Python27/Lib/site-packages/autobahn/case/case1_1_1.py +31 -0
  10. Python27/Lib/site-packages/autobahn/case/case1_1_1.pyc +0 -0
  11. Python27/Lib/site-packages/autobahn/case/case1_1_2.py +31 -0
  12. Python27/Lib/site-packages/autobahn/case/case1_1_2.pyc +0 -0
  13. Python27/Lib/site-packages/autobahn/case/case1_1_3.py +31 -0
  14. Python27/Lib/site-packages/autobahn/case/case1_1_3.pyc +0 -0
  15. Python27/Lib/site-packages/autobahn/case/case1_1_4.py +31 -0
  16. Python27/Lib/site-packages/autobahn/case/case1_1_4.pyc +0 -0
  17. Python27/Lib/site-packages/autobahn/case/case1_1_5.py +31 -0
  18. Python27/Lib/site-packages/autobahn/case/case1_1_5.pyc +0 -0
  19. Python27/Lib/site-packages/autobahn/case/case1_1_6.py +31 -0
  20. Python27/Lib/site-packages/autobahn/case/case1_1_6.pyc +0 -0
  21. Python27/Lib/site-packages/autobahn/case/case1_1_7.py +31 -0
  22. Python27/Lib/site-packages/autobahn/case/case1_1_7.pyc +0 -0
  23. Python27/Lib/site-packages/autobahn/case/case1_1_8.py +31 -0
  24. Python27/Lib/site-packages/autobahn/case/case1_1_8.pyc +0 -0
  25. Python27/Lib/site-packages/autobahn/case/case1_2_1.py +31 -0
  26. Python27/Lib/site-packages/autobahn/case/case1_2_1.pyc +0 -0
  27. Python27/Lib/site-packages/autobahn/case/case1_2_2.py +31 -0
  28. Python27/Lib/site-packages/autobahn/case/case1_2_2.pyc +0 -0
  29. Python27/Lib/site-packages/autobahn/case/case1_2_3.py +31 -0
  30. Python27/Lib/site-packages/autobahn/case/case1_2_3.pyc +0 -0
  31. Python27/Lib/site-packages/autobahn/case/case1_2_4.py +31 -0
  32. Python27/Lib/site-packages/autobahn/case/case1_2_4.pyc +0 -0
  33. Python27/Lib/site-packages/autobahn/case/case1_2_5.py +31 -0
  34. Python27/Lib/site-packages/autobahn/case/case1_2_5.pyc +0 -0
  35. Python27/Lib/site-packages/autobahn/case/case1_2_6.py +31 -0
  36. Python27/Lib/site-packages/autobahn/case/case1_2_6.pyc +0 -0
  37. Python27/Lib/site-packages/autobahn/case/case1_2_7.py +31 -0
  38. Python27/Lib/site-packages/autobahn/case/case1_2_7.pyc +0 -0
  39. Python27/Lib/site-packages/autobahn/case/case1_2_8.py +31 -0
  40. Python27/Lib/site-packages/autobahn/case/case1_2_8.pyc +0 -0
  41. Python27/Lib/site-packages/autobahn/case/case2_1.py +30 -0
  42. Python27/Lib/site-packages/autobahn/case/case2_1.pyc +0 -0
  43. Python27/Lib/site-packages/autobahn/case/case2_10.py +37 -0
  44. Python27/Lib/site-packages/autobahn/case/case2_10.pyc +0 -0
  45. Python27/Lib/site-packages/autobahn/case/case2_11.py +28 -0
  46. Python27/Lib/site-packages/autobahn/case/case2_11.pyc +0 -0
  47. Python27/Lib/site-packages/autobahn/case/case2_2.py +31 -0
  48. Python27/Lib/site-packages/autobahn/case/case2_2.pyc +0 -0
  49. Python27/Lib/site-packages/autobahn/case/case2_3.py +31 -0
  50. Python27/Lib/site-packages/autobahn/case/case2_3.pyc +0 -0
  51. Python27/Lib/site-packages/autobahn/case/case2_4.py +31 -0
  52. Python27/Lib/site-packages/autobahn/case/case2_4.pyc +0 -0
  53. Python27/Lib/site-packages/autobahn/case/case2_5.py +31 -0
  54. Python27/Lib/site-packages/autobahn/case/case2_5.pyc +0 -0
  55. Python27/Lib/site-packages/autobahn/case/case2_6.py +31 -0
  56. Python27/Lib/site-packages/autobahn/case/case2_6.pyc +0 -0
  57. Python27/Lib/site-packages/autobahn/case/case2_7.py +30 -0
  58. Python27/Lib/site-packages/autobahn/case/case2_7.pyc +0 -0
  59. Python27/Lib/site-packages/autobahn/case/case2_8.py +30 -0
  60. Python27/Lib/site-packages/autobahn/case/case2_8.pyc +0 -0
  61. Python27/Lib/site-packages/autobahn/case/case2_9.py +32 -0
  62. Python27/Lib/site-packages/autobahn/case/case2_9.pyc +0 -0
  63. Python27/Lib/site-packages/autobahn/case/case3_1.py +30 -0
  64. Python27/Lib/site-packages/autobahn/case/case3_1.pyc +0 -0
  65. Python27/Lib/site-packages/autobahn/case/case3_2.py +34 -0
  66. Python27/Lib/site-packages/autobahn/case/case3_2.pyc +0 -0
  67. Python27/Lib/site-packages/autobahn/case/case3_3.py +34 -0
  68. Python27/Lib/site-packages/autobahn/case/case3_3.pyc +0 -0
  69. Python27/Lib/site-packages/autobahn/case/case3_4.py +34 -0
  70. Python27/Lib/site-packages/autobahn/case/case3_4.pyc +0 -0
  71. Python27/Lib/site-packages/autobahn/case/case3_5.py +31 -0
  72. Python27/Lib/site-packages/autobahn/case/case3_5.pyc +0 -0
  73. Python27/Lib/site-packages/autobahn/case/case3_6.py +31 -0
  74. Python27/Lib/site-packages/autobahn/case/case3_6.pyc +0 -0
  75. Python27/Lib/site-packages/autobahn/case/case3_7.py +30 -0
  76. Python27/Lib/site-packages/autobahn/case/case3_7.pyc +0 -0
  77. Python27/Lib/site-packages/autobahn/case/case4_1_1.py +30 -0
  78. Python27/Lib/site-packages/autobahn/case/case4_1_1.pyc +0 -0
  79. Python27/Lib/site-packages/autobahn/case/case4_1_2.py +30 -0
  80. Python27/Lib/site-packages/autobahn/case/case4_1_2.pyc +0 -0
  81. Python27/Lib/site-packages/autobahn/case/case4_1_3.py +34 -0
  82. Python27/Lib/site-packages/autobahn/case/case4_1_3.pyc +0 -0
  83. Python27/Lib/site-packages/autobahn/case/case4_1_4.py +34 -0
  84. Python27/Lib/site-packages/autobahn/case/case4_1_4.pyc +0 -0
  85. Python27/Lib/site-packages/autobahn/case/case4_1_5.py +34 -0
  86. Python27/Lib/site-packages/autobahn/case/case4_1_5.pyc +0 -0
  87. Python27/Lib/site-packages/autobahn/case/case4_2_1.py +30 -0
  88. Python27/Lib/site-packages/autobahn/case/case4_2_1.pyc +0 -0
  89. Python27/Lib/site-packages/autobahn/case/case4_2_2.py +30 -0
  90. Python27/Lib/site-packages/autobahn/case/case4_2_2.pyc +0 -0
  91. Python27/Lib/site-packages/autobahn/case/case4_2_3.py +34 -0
  92. Python27/Lib/site-packages/autobahn/case/case4_2_3.pyc +0 -0
  93. Python27/Lib/site-packages/autobahn/case/case4_2_4.py +34 -0
  94. Python27/Lib/site-packages/autobahn/case/case4_2_4.pyc +0 -0
  95. Python27/Lib/site-packages/autobahn/case/case4_2_5.py +34 -0
  96. Python27/Lib/site-packages/autobahn/case/case4_2_5.pyc +0 -0
  97. Python27/Lib/site-packages/autobahn/case/case5_1.py +31 -0
  98. Python27/Lib/site-packages/autobahn/case/case5_1.pyc +0 -0
  99. Python27/Lib/site-packages/autobahn/case/case5_10.py +31 -0
  100. Python27/Lib/site-packages/autobahn/case/case5_10.pyc +0 -0
  101. Python27/Lib/site-packages/autobahn/case/case5_11.py +31 -0
  102. Python27/Lib/site-packages/autobahn/case/case5_11.pyc +0 -0
  103. Python27/Lib/site-packages/autobahn/case/case5_12.py +31 -0
  104. Python27/Lib/site-packages/autobahn/case/case5_12.pyc +0 -0
  105. Python27/Lib/site-packages/autobahn/case/case5_13.py +31 -0
  106. Python27/Lib/site-packages/autobahn/case/case5_13.pyc +0 -0
  107. Python27/Lib/site-packages/autobahn/case/case5_14.py +31 -0
  108. Python27/Lib/site-packages/autobahn/case/case5_14.pyc +0 -0
  109. Python27/Lib/site-packages/autobahn/case/case5_15.py +35 -0
  110. Python27/Lib/site-packages/autobahn/case/case5_15.pyc +0 -0
  111. Python27/Lib/site-packages/autobahn/case/case5_16.py +33 -0
  112. Python27/Lib/site-packages/autobahn/case/case5_16.pyc +0 -0
  113. Python27/Lib/site-packages/autobahn/case/case5_17.py +33 -0
  114. Python27/Lib/site-packages/autobahn/case/case5_17.pyc +0 -0
  115. Python27/Lib/site-packages/autobahn/case/case5_18.py +31 -0
  116. Python27/Lib/site-packages/autobahn/case/case5_18.pyc +0 -0
  117. Python27/Lib/site-packages/autobahn/case/case5_19.py +56 -0
  118. Python27/Lib/site-packages/autobahn/case/case5_19.pyc +0 -0
  119. Python27/Lib/site-packages/autobahn/case/case5_2.py +31 -0
  120. Python27/Lib/site-packages/autobahn/case/case5_2.pyc +0 -0
  121. Python27/Lib/site-packages/autobahn/case/case5_20.py +31 -0
  122. Python27/Lib/site-packages/autobahn/case/case5_20.pyc +0 -0
  123. Python27/Lib/site-packages/autobahn/case/case5_3.py +32 -0
  124. Python27/Lib/site-packages/autobahn/case/case5_3.pyc +0 -0
  125. Python27/Lib/site-packages/autobahn/case/case5_4.py +32 -0
  126. Python27/Lib/site-packages/autobahn/case/case5_4.pyc +0 -0
  127. Python27/Lib/site-packages/autobahn/case/case5_5.py +32 -0
  128. Python27/Lib/site-packages/autobahn/case/case5_5.pyc +0 -0
  129. Python27/Lib/site-packages/autobahn/case/case5_6.py +34 -0
  130. Python27/Lib/site-packages/autobahn/case/case5_6.pyc +0 -0
  131. Python27/Lib/site-packages/autobahn/case/case5_7.py +34 -0
  132. Python27/Lib/site-packages/autobahn/case/case5_7.pyc +0 -0
  133. Python27/Lib/site-packages/autobahn/case/case5_8.py +34 -0
  134. Python27/Lib/site-packages/autobahn/case/case5_8.pyc +0 -0
  135. Python27/Lib/site-packages/autobahn/case/case5_9.py +31 -0
  136. Python27/Lib/site-packages/autobahn/case/case5_9.pyc +0 -0
  137. Python27/Lib/site-packages/autobahn/case/case6_1.py +30 -0
  138. Python27/Lib/site-packages/autobahn/case/case6_1.pyc +0 -0
  139. Python27/Lib/site-packages/autobahn/case/case6_2.py +32 -0
  140. Python27/Lib/site-packages/autobahn/case/case6_2.pyc +0 -0
  141. Python27/Lib/site-packages/autobahn/case/case6_3.py +33 -0
  142. Python27/Lib/site-packages/autobahn/case/case6_3.pyc +0 -0
  143. Python27/Lib/site-packages/autobahn/case/case9_1_1.py +54 -0
  144. Python27/Lib/site-packages/autobahn/case/case9_1_1.pyc +0 -0
  145. Python27/Lib/site-packages/autobahn/case/case9_1_2.py +31 -0
  146. Python27/Lib/site-packages/autobahn/case/case9_1_2.pyc +0 -0
  147. Python27/Lib/site-packages/autobahn/case/case9_1_3.py +31 -0
  148. Python27/Lib/site-packages/autobahn/case/case9_1_3.pyc +0 -0
  149. Python27/Lib/site-packages/autobahn/case/case9_1_4.py +31 -0
  150. Python27/Lib/site-packages/autobahn/case/case9_1_4.pyc +0 -0
  151. Python27/Lib/site-packages/autobahn/case/case9_1_5.py +31 -0
  152. Python27/Lib/site-packages/autobahn/case/case9_1_5.pyc +0 -0
  153. Python27/Lib/site-packages/autobahn/case/case9_1_6.py +31 -0
  154. Python27/Lib/site-packages/autobahn/case/case9_1_6.pyc +0 -0
  155. Python27/Lib/site-packages/autobahn/case/case9_2_1.py +54 -0
  156. Python27/Lib/site-packages/autobahn/case/case9_2_1.pyc +0 -0
  157. Python27/Lib/site-packages/autobahn/case/case9_2_2.py +31 -0
  158. Python27/Lib/site-packages/autobahn/case/case9_2_2.pyc +0 -0
  159. Python27/Lib/site-packages/autobahn/case/case9_2_3.py +31 -0
  160. Python27/Lib/site-packages/autobahn/case/case9_2_3.pyc +0 -0
  161. Python27/Lib/site-packages/autobahn/case/case9_2_4.py +31 -0
  162. Python27/Lib/site-packages/autobahn/case/case9_2_4.pyc +0 -0
  163. Python27/Lib/site-packages/autobahn/case/case9_2_5.py +31 -0
  164. Python27/Lib/site-packages/autobahn/case/case9_2_5.pyc +0 -0
  165. Python27/Lib/site-packages/autobahn/case/case9_2_6.py +31 -0
  166. Python27/Lib/site-packages/autobahn/case/case9_2_6.pyc +0 -0
  167. Python27/Lib/site-packages/autobahn/case/case9_3_1.py +55 -0
  168. Python27/Lib/site-packages/autobahn/case/case9_3_1.pyc +0 -0
  169. Python27/Lib/site-packages/autobahn/case/case9_3_2.py +32 -0
  170. Python27/Lib/site-packages/autobahn/case/case9_3_2.pyc +0 -0
  171. Python27/Lib/site-packages/autobahn/case/case9_3_3.py +32 -0
  172. Python27/Lib/site-packages/autobahn/case/case9_3_3.pyc +0 -0
  173. Python27/Lib/site-packages/autobahn/case/case9_3_4.py +32 -0
  174. Python27/Lib/site-packages/autobahn/case/case9_3_4.pyc +0 -0
  175. Python27/Lib/site-packages/autobahn/case/case9_3_5.py +32 -0
  176. Python27/Lib/site-packages/autobahn/case/case9_3_5.pyc +0 -0
  177. Python27/Lib/site-packages/autobahn/case/case9_3_6.py +32 -0
  178. Python27/Lib/site-packages/autobahn/case/case9_3_6.pyc +0 -0
  179. Python27/Lib/site-packages/autobahn/case/case9_3_7.py +32 -0
  180. Python27/Lib/site-packages/autobahn/case/case9_3_7.pyc +0 -0
  181. Python27/Lib/site-packages/autobahn/case/case9_3_8.py +32 -0
  182. Python27/Lib/site-packages/autobahn/case/case9_3_8.pyc +0 -0
  183. Python27/Lib/site-packages/autobahn/case/case9_3_9.py +32 -0
  184. Python27/Lib/site-packages/autobahn/case/case9_3_9.pyc +0 -0
  185. Python27/Lib/site-packages/autobahn/case/case9_4_1.py +55 -0
  186. Python27/Lib/site-packages/autobahn/case/case9_4_1.pyc +0 -0
  187. Python27/Lib/site-packages/autobahn/case/case9_4_2.py +32 -0
  188. Python27/Lib/site-packages/autobahn/case/case9_4_2.pyc +0 -0
  189. Python27/Lib/site-packages/autobahn/case/case9_4_3.py +32 -0
  190. Python27/Lib/site-packages/autobahn/case/case9_4_3.pyc +0 -0
  191. Python27/Lib/site-packages/autobahn/case/case9_4_4.py +32 -0
  192. Python27/Lib/site-packages/autobahn/case/case9_4_4.pyc +0 -0
  193. Python27/Lib/site-packages/autobahn/case/case9_4_5.py +32 -0
  194. Python27/Lib/site-packages/autobahn/case/case9_4_5.pyc +0 -0
  195. Python27/Lib/site-packages/autobahn/case/case9_4_6.py +32 -0
  196. Python27/Lib/site-packages/autobahn/case/case9_4_6.pyc +0 -0
  197. Python27/Lib/site-packages/autobahn/case/case9_4_7.py +32 -0
  198. Python27/Lib/site-packages/autobahn/case/case9_4_7.pyc +0 -0
  199. Python27/Lib/site-packages/autobahn/case/case9_4_8.py +32 -0
  200. Python27/Lib/site-packages/autobahn/case/case9_4_8.pyc +0 -0
  201. Python27/Lib/site-packages/autobahn/case/case9_4_9.py +32 -0
  202. Python27/Lib/site-packages/autobahn/case/case9_4_9.pyc +0 -0
  203. Python27/Lib/site-packages/autobahn/case/case9_5_1.py +58 -0
  204. Python27/Lib/site-packages/autobahn/case/case9_5_1.pyc +0 -0
  205. Python27/Lib/site-packages/autobahn/case/case9_5_2.py +28 -0
  206. Python27/Lib/site-packages/autobahn/case/case9_5_2.pyc +0 -0
  207. Python27/Lib/site-packages/autobahn/case/case9_5_3.py +28 -0
  208. Python27/Lib/site-packages/autobahn/case/case9_5_3.pyc +0 -0
  209. Python27/Lib/site-packages/autobahn/case/case9_5_4.py +28 -0
  210. Python27/Lib/site-packages/autobahn/case/case9_5_4.pyc +0 -0
  211. Python27/Lib/site-packages/autobahn/case/case9_5_5.py +28 -0
  212. Python27/Lib/site-packages/autobahn/case/case9_5_5.pyc +0 -0
  213. Python27/Lib/site-packages/autobahn/case/case9_5_6.py +28 -0
  214. Python27/Lib/site-packages/autobahn/case/case9_5_6.pyc +0 -0
  215. Python27/Lib/site-packages/autobahn/case/case9_6_1.py +58 -0
  216. Python27/Lib/site-packages/autobahn/case/case9_6_1.pyc +0 -0
  217. Python27/Lib/site-packages/autobahn/case/case9_6_2.py +28 -0
  218. Python27/Lib/site-packages/autobahn/case/case9_6_2.pyc +0 -0
  219. Python27/Lib/site-packages/autobahn/case/case9_6_3.py +28 -0
  220. Python27/Lib/site-packages/autobahn/case/case9_6_3.pyc +0 -0
  221. Python27/Lib/site-packages/autobahn/case/case9_6_4.py +28 -0
  222. Python27/Lib/site-packages/autobahn/case/case9_6_4.pyc +0 -0
  223. Python27/Lib/site-packages/autobahn/case/case9_6_5.py +28 -0
  224. Python27/Lib/site-packages/autobahn/case/case9_6_5.pyc +0 -0
  225. Python27/Lib/site-packages/autobahn/case/case9_6_6.py +28 -0
  226. Python27/Lib/site-packages/autobahn/case/case9_6_6.pyc +0 -0
  227. Python27/Lib/site-packages/autobahn/fuzzing.py +911 -0
  228. Python27/Lib/site-packages/autobahn/fuzzing.pyc +0 -0
  229. Python27/Lib/site-packages/autobahn/websocket.py +1505 -0
  230. Python27/Lib/site-packages/autobahn/websocket.pyc +0 -0
  231. Python27/Lib/site-packages/autobahn-0.3.1-py2.7.egg-info/PKG-INFO +10 -0
  232. Python27/Lib/site-packages/autobahn-0.3.1-py2.7.egg-info/SOURCES.txt +124 -0
  233. Python27/Lib/site-packages/autobahn-0.3.1-py2.7.egg-info/dependency_links.txt +1 -0
  234. Python27/Lib/site-packages/autobahn-0.3.1-py2.7.egg-info/not-zip-safe +1 -0
  235. Python27/Lib/site-packages/autobahn-0.3.1-py2.7.egg-info/requires.txt +1 -0
  236. Python27/Lib/site-packages/autobahn-0.3.1-py2.7.egg-info/top_level.txt +1 -0
@@ -0,0 +1,1505 @@
1
+ ###############################################################################
2
+ ##
3
+ ## Copyright 2011 Tavendo GmbH
4
+ ##
5
+ ## Licensed under the Apache License, Version 2.0 (the "License");
6
+ ## you may not use this file except in compliance with the License.
7
+ ## You may obtain a copy of the License at
8
+ ##
9
+ ## http://www.apache.org/licenses/LICENSE-2.0
10
+ ##
11
+ ## Unless required by applicable law or agreed to in writing, software
12
+ ## distributed under the License is distributed on an "AS IS" BASIS,
13
+ ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ ## See the License for the specific language governing permissions and
15
+ ## limitations under the License.
16
+ ##
17
+ ###############################################################################
18
+
19
+ from twisted.internet import reactor, protocol
20
+ from twisted.python import log
21
+ import binascii
22
+ import hashlib
23
+ import base64
24
+ import struct
25
+ import random
26
+ import urlparse
27
+ import os
28
+ import cStringIO
29
+ import StringIO
30
+ from io import BytesIO
31
+
32
+
33
+ class FrameHeader:
34
+
35
+ def __init__(self, opcode, fin, rsv, length, mask, mask_array):
36
+ self.opcode = opcode
37
+ self.fin = fin
38
+ self.rsv = rsv
39
+ self.length = length
40
+ self.ptr = 0
41
+ self.mask = mask
42
+ self.mask_array = mask_array
43
+
44
+
45
+ class WebSocketProtocol(protocol.Protocol):
46
+ """
47
+ A Twisted Protocol class for WebSockets. This class is used by both WebSocket
48
+ client and server protocol version. It is unusable standalone, for example
49
+ the WebSockets initial handshake is implemented in derived class differently
50
+ for clients and servers.
51
+ """
52
+
53
+ ## magic used during WebSocket handshake
54
+ ##
55
+ WS_MAGIC = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
56
+
57
+ MESSAGE_TYPE_TEXT = 1
58
+ """WebSockets text message type (UTF-8 payload)."""
59
+
60
+ MESSAGE_TYPE_BINARY = 2
61
+ """WebSockets binary message type (arbitrary binary payload)."""
62
+
63
+ ## WebSockets protocol state
64
+ ##
65
+ STATE_CLOSED = 0
66
+ STATE_CONNECTING = 1
67
+ STATE_CLOSING = 2
68
+ STATE_OPEN = 3
69
+
70
+ ## Streaming Send State
71
+ SEND_STATE_GROUND = 0
72
+ SEND_STATE_MESSAGE_BEGIN = 1
73
+ SEND_STATE_INSIDE_MESSAGE = 2
74
+ SEND_STATE_INSIDE_MESSAGE_FRAME = 3
75
+
76
+ ## WebSockets protocol close codes
77
+ ##
78
+ CLOSE_STATUS_CODE_NORMAL = 1000
79
+ """Normal close of connection."""
80
+
81
+ CLOSE_STATUS_CODE_GOING_AWAY = 1001
82
+ """Going away."""
83
+
84
+ CLOSE_STATUS_CODE_PROTOCOL_ERROR = 1002
85
+ """Protocol error."""
86
+
87
+ CLOSE_STATUS_CODE_PAYLOAD_NOT_ACCEPTED = 1003
88
+ """Payload not accepted."""
89
+
90
+ CLOSE_STATUS_CODE_FRAME_TOO_LARGE = 1004
91
+ """Frame too large."""
92
+
93
+ CLOSE_STATUS_CODE_NULL = 1005 # MUST NOT be set in close frame!
94
+ """No status received. (MUST NOT be used as status code when sending a close)."""
95
+
96
+ CLOSE_STATUS_CODE_CONNECTION_LOST = 1006 # MUST NOT be set in close frame!
97
+ """Abnormal close of connection. (MUST NOT be used as status code when sending a close)."""
98
+
99
+ CLOSE_STATUS_CODE_TEXT_FRAME_NOT_UTF8 = 1007
100
+ """Invalid UTF-8."""
101
+
102
+
103
+ def onOpen(self):
104
+ """
105
+ Callback when initial WebSockets handshake was completed. Now you may send messages.
106
+ Default implementation does nothing. Override in derived class.
107
+ """
108
+ if self.debug:
109
+ log.msg("WebSocketProtocol.onOpen")
110
+
111
+
112
+ def onMessageBegin(self, opcode):
113
+ """
114
+ Callback when receiving a new message has begun. Default implementation will
115
+ prepare to buffer message frames. Override in derived class.
116
+
117
+ :param opcode: Opcode of message.
118
+ :type opcode: int
119
+ """
120
+ self.message_opcode = opcode
121
+ self.message_data = []
122
+
123
+
124
+ def onMessageFrameBegin(self, length, reserved):
125
+ """
126
+ Callback when receiving a new message frame has begun. Default implementation will
127
+ prepare to buffer message frame data. Override in derived class.
128
+
129
+ :param length: Length of message frame which is received.
130
+ :type length: int
131
+ :param reserved: Reserved bits set in frame (an integer from 0 to 7).
132
+ :type reserved: int
133
+ """
134
+ self.frame_length = length
135
+ self.frame_reserved = reserved
136
+ self.frame_data = []
137
+
138
+
139
+ def onMessageFrameData(self, payload):
140
+ """
141
+ Callback when receiving data witin message frame. Default implementation will
142
+ buffer data for frame. Override in derived class.
143
+
144
+ :param payload: Partial payload for message frame.
145
+ :type payload: str
146
+ """
147
+ self.frame_data.append(payload)
148
+
149
+
150
+ def onMessageFrameEnd(self):
151
+ """
152
+ Callback when a message frame has been completely received. Default implementation
153
+ will flatten the buffered frame data and callback onMessageFrame. Override
154
+ in derived class.
155
+ """
156
+ data = bytearray().join(self.frame_data)
157
+ self.logRxFrame(self.current_frame.fin, self.current_frame.rsv, self.current_frame.opcode, self.current_frame.mask is not None, self.current_frame.length, self.current_frame.mask, data)
158
+ self.onMessageFrame(data, self.frame_reserved)
159
+
160
+
161
+ def onMessageFrame(self, payload, reserved):
162
+ """
163
+ Callback fired when complete message frame has been received. Default implementation
164
+ will buffer frame for message. Override in derived class.
165
+
166
+ :param payload: Message frame payload.
167
+ :type payload: str
168
+ :param reserved: Reserved bits set in frame (an integer from 0 to 7).
169
+ :type reserved: int
170
+ """
171
+ self.message_data.append(payload)
172
+
173
+
174
+ def onMessageEnd(self):
175
+ """
176
+ Callback when a message has been completely received. Default implementation
177
+ will flatten the buffered frames and callback onMessage. Override
178
+ in derived class.
179
+ """
180
+ data = bytearray().join(self.message_data)
181
+ self.onMessage(str(data), self.message_opcode == WebSocketProtocol.MESSAGE_TYPE_BINARY)
182
+ self.message_opcode = None
183
+ self.message_data = None
184
+
185
+
186
+ def onMessage(self, payload, binary):
187
+ """
188
+ Callback when a complete message was received. Default implementation does nothing.
189
+ Override in derived class.
190
+
191
+ :param payload: Message payload (UTF-8 encoded text string or binary string). Can also be an empty string, when message contained no payload.
192
+ :type payload: str
193
+ :param binary: If True, payload is binary, otherwise text.
194
+ :type binary: bool
195
+ """
196
+ if self.debug:
197
+ log.msg("WebSocketProtocol.onMessage")
198
+
199
+
200
+ def onPing(self, payload):
201
+ """
202
+ Callback when Ping was received. Default implementation responds
203
+ with a Pong. Override in derived class.
204
+
205
+ :param payload: Payload of Ping, when there was any. Can be arbitrary, up to 125 octets.
206
+ :type payload: str
207
+ """
208
+ if self.debug:
209
+ log.msg("WebSocketProtocol.onPing")
210
+ self.sendPong(payload)
211
+
212
+
213
+ def onPong(self, payload):
214
+ """
215
+ Callback when Pong was received. Default implementation does nothing.
216
+ Override in derived class.
217
+
218
+ :param payload: Payload of Pong, when there was any. Can be arbitrary, up to 125 octets.
219
+ """
220
+ if self.debug:
221
+ log.msg("WebSocketProtocol.onPong")
222
+
223
+
224
+ def onClose(self, code, reason):
225
+ """
226
+ Callback when Close was received. The default implementation answers by
227
+ sending a normal Close when no Close was sent before. Otherwise it drops
228
+ the connection. Override in derived class.
229
+
230
+ :param code: None or close status code, if there was one (:class:`WebSocketProtocol`.CLOSE_STATUS_CODE_*).
231
+ :type code: int
232
+ :param reason: None or close reason (when present, a status code MUST have been also be present).
233
+ :type reason: str
234
+ """
235
+ if self.debug:
236
+ log.msg("WebSocketProtocol.onClose")
237
+ if self.closeAlreadySent:
238
+ self.transport.loseConnection()
239
+ else:
240
+ self.sendClose(code = WebSocketProtocol.CLOSE_STATUS_CODE_NORMAL)
241
+
242
+
243
+ def __init__(self):
244
+ self.state = WebSocketProtocol.STATE_CLOSED
245
+ self.send_state = WebSocketProtocol.SEND_STATE_GROUND
246
+
247
+
248
+ def getState(self):
249
+ return self.state
250
+
251
+
252
+ def failConnection(self):
253
+ self.failedByMe = True
254
+ self.state = WebSocketProtocol.STATE_CLOSED
255
+ self.transport.loseConnection()
256
+
257
+
258
+ def protocolViolation(self, reason):
259
+ if self.debug:
260
+ log.msg("Failing connection on protocol violation : %s" % reason)
261
+ self.failConnection()
262
+
263
+
264
+ def connectionMade(self):
265
+ """
266
+ This is called by Twisted framework when a new TCP connection has been established
267
+ and handed over to a Protocol instance (an instance of this class).
268
+ """
269
+ self.debug = self.factory.debug
270
+ self.transport.setTcpNoDelay(True)
271
+ self.peer = self.transport.getPeer()
272
+ self.peerstr = "%s:%d" % (self.peer.host, self.peer.port)
273
+ self.state = WebSocketProtocol.STATE_CONNECTING
274
+ self.send_state = WebSocketProtocol.SEND_STATE_GROUND
275
+ self.data = ""
276
+ self.closeAlreadySent = False
277
+ self.failedByMe = False
278
+
279
+
280
+ def connectionLost(self, reason):
281
+ """
282
+ This is called by Twisted framework when a TCP connection was lost.
283
+ """
284
+ self.state = WebSocketProtocol.STATE_CLOSED
285
+
286
+
287
+ def logRxOctets(self, data):
288
+ if self.debug:
289
+ d = str(buffer(data))
290
+ log.msg("RX Octets from %s : %s" % (self.peerstr, binascii.b2a_hex(d)))
291
+
292
+
293
+ def logTxOctets(self, data, sync):
294
+ if self.debug:
295
+ d = str(buffer(data))
296
+ log.msg("TX Octets to %s : %s" % (self.peerstr, binascii.b2a_hex(d)))
297
+
298
+
299
+ def logRxFrame(self, fin, rsv, opcode, masked, payload_len, mask, payload):
300
+ if self.debug:
301
+ d = str(buffer(payload))
302
+ if mask:
303
+ mmask = binascii.b2a_hex(mask)
304
+ else:
305
+ mmask = str(mask)
306
+ log.msg("RX Frame from %s : fin = %s, rsv = %s, opcode = %s, masked = %s, payload_len = %s, mask = %s, payload = %s" % (self.peerstr, str(fin), str(rsv), str(opcode), str(masked), str(payload_len), mmask, binascii.b2a_hex(d)))
307
+
308
+
309
+ def logTxFrame(self, opcode, payload, fin, rsv, mask, payload_len, chopsize, sync):
310
+ if self.debug:
311
+ d = str(buffer(payload))
312
+ if mask:
313
+ mmask = binascii.b2a_hex(mask)
314
+ else:
315
+ mmask = str(mask)
316
+ log.msg("TX Frame to %s : fin = %s, rsv = %s, opcode = %s, mask = %s, payload_len = %s, chopsize = %s, sync = %s, payload = %s" % (self.peerstr, str(fin), str(rsv), str(opcode), mmask, str(payload_len), str(chopsize), str(sync), binascii.b2a_hex(d)))
317
+
318
+
319
+ def dataReceived(self, data):
320
+ """
321
+ This is called by Twisted framework upon receiving data on TCP connection.
322
+ """
323
+ self.logRxOctets(data)
324
+
325
+ self.data += data
326
+ self.consumeData()
327
+
328
+
329
+ def consumeData(self):
330
+
331
+ buffered_len = len(self.data)
332
+
333
+ ## WebSocket is open (handshake was completed) or close was sent
334
+ ##
335
+ if self.state in [WebSocketProtocol.STATE_OPEN, WebSocketProtocol.STATE_CLOSING]:
336
+
337
+ while self.processData():
338
+ pass
339
+
340
+ ## WebSocket needs handshake
341
+ ##
342
+ elif self.state == WebSocketProtocol.STATE_CONNECTING:
343
+
344
+ ## the implementation of processHandshake() in derived
345
+ ## class needs to perform client or server handshake
346
+ ## from other party here ..
347
+ ##
348
+ self.processHandshake()
349
+
350
+ ## we failed the connection .. don't process any more data!
351
+ ##
352
+ elif self.state == WebSocketProtocol.STATE_CLOSED:
353
+ log.msg("received data in STATE_CLOSED")
354
+
355
+ ## should not arrive here (invalid state)
356
+ ##
357
+ else:
358
+ raise Exception("invalid state")
359
+
360
+
361
+ def processHandshake(self):
362
+ """
363
+ Process WebSockets handshake.
364
+ """
365
+ raise Exception("must implement handshake (client or server) in derived class")
366
+
367
+
368
+ def syncSocket(self):
369
+
370
+ ## FIXME: find suitable replacement for this code, which appears to break
371
+ ## sometimes ..
372
+ ##
373
+ ## From the web: "You should never call reactor.doSelect. This isn't portable across
374
+ ## reactors, and it could easily break the reactor by re-entering it where it isn't
375
+ ## expecting to be re-entered."
376
+ ##
377
+ try:
378
+ reactor.doIteration(0)
379
+ return True
380
+ except:
381
+ return False # socket has already gone away ..
382
+
383
+
384
+ def registerProducer(self, producer, streaming):
385
+ self.transport.registerProducer(producer, streaming)
386
+
387
+
388
+ def sendData(self, raw, sync = False, chopsize = None):
389
+ """
390
+ Wrapper for self.transport.write which allows to give a chopsize.
391
+ When asked to chop up writing to TCP stream, we write only chopsize octets
392
+ and then give up control to select() in underlying reactor so that bytes
393
+ get onto wire immediately. Note that this is different from and unrelated
394
+ to WebSockets data message fragmentation. Note that this is also different
395
+ from the TcpNoDelay option which can be set on the socket.
396
+ """
397
+ if chopsize and chopsize > 0:
398
+ i = 0
399
+ n = len(raw)
400
+ done = False
401
+ while not done:
402
+ j = i + chopsize
403
+ if j >= n:
404
+ done = True
405
+ j = n
406
+ self.logTxOctets(raw[i:j], True)
407
+ self.transport.write(raw[i:j])
408
+
409
+ ## This is where the "magic" happens. We give up control to the
410
+ ## Twisted reactor, which calls into the OS Select(), which will
411
+ ## then send out outstanding data. I suspect this Twisted call is
412
+ ## probably not "intended" to be called by Twisted users, but it is
413
+ ## the only "way" I found to work to attain the intended result.
414
+ ##
415
+ self.syncSocket()
416
+
417
+ i += chopsize
418
+ else:
419
+ self.logTxOctets(raw, sync)
420
+ self.transport.write(raw)
421
+ if sync:
422
+ self.syncSocket()
423
+
424
+
425
+ def processData(self):
426
+ """
427
+ After WebSockets handshake has been completed, this procedure will do all
428
+ subsequent processing of incoming bytes.
429
+ """
430
+ buffered_len = len(self.data)
431
+
432
+ ## outside a frame, that is we are awaiting data which starts a new frame
433
+ ##
434
+ if self.current_frame is None:
435
+
436
+ #buffered_len = len(self.data)
437
+
438
+ ## need minimum of 2 octets to for new frame
439
+ ##
440
+ if buffered_len >= 2:
441
+
442
+ ## FIN, RSV, OPCODE
443
+ ##
444
+ b = ord(self.data[0])
445
+ frame_fin = (b & 0x80) != 0
446
+ frame_rsv = (b & 0x70) >> 4
447
+ frame_opcode = b & 0x0f
448
+
449
+ ## MASK, PAYLOAD LEN 1
450
+ ##
451
+ b = ord(self.data[1])
452
+ frame_masked = (b & 0x80) != 0
453
+ frame_payload_len1 = b & 0x7f
454
+
455
+ ## MUST be 0 when no extension defining
456
+ ## the semantics of RSV has been negotiated
457
+ ##
458
+ if frame_rsv != 0:
459
+ self.protocolViolation("RSV != 0 and no extension negotiated")
460
+ return False
461
+
462
+ ## all client-to-server frames MUST be masked
463
+ ##
464
+ if self.isServer and not frame_masked:
465
+ self.protocolViolation("unmasked client to server frame")
466
+ return False
467
+
468
+ ## check frame
469
+ ##
470
+ if frame_opcode > 7: # control frame (have MSB in opcode set)
471
+
472
+ ## control frames MUST NOT be fragmented
473
+ ##
474
+ if not frame_fin:
475
+ self.protocolViolation("fragmented control frame")
476
+ return False
477
+
478
+ ## control frames MUST have payload 125 octets or less
479
+ ##
480
+ if frame_payload_len1 > 125:
481
+ self.protocolViolation("control frame with payload length > 125 octets")
482
+ return False
483
+
484
+ ## check for reserved control frame opcodes
485
+ ##
486
+ if frame_opcode not in [8, 9, 10]:
487
+ self.protocolViolation("control frame using reserved opcode %d" % frame_opcode)
488
+ return False
489
+
490
+ ## close frame : if there is a body, the first two bytes of the body MUST be a 2-byte
491
+ ## unsigned integer (in network byte order) representing a status code
492
+ ##
493
+ if frame_opcode == 8 and frame_payload_len1 == 1:
494
+ self.protocolViolation("received close control frame with payload len 1")
495
+ return False
496
+
497
+ else: # data frame
498
+
499
+ ## check for reserved data frame opcodes
500
+ ##
501
+ if frame_opcode not in [0, 1, 2]:
502
+ self.protocolViolation("data frame using reserved opcode %d" % frame_opcode)
503
+ return False
504
+
505
+ ## check opcode vs message fragmentation state 1/2
506
+ ##
507
+ if not self.inside_message and frame_opcode == 0:
508
+ self.protocolViolation("received continuation data frame outside fragmented message")
509
+ return False
510
+
511
+ ## check opcode vs message fragmentation state 2/2
512
+ ##
513
+ if self.inside_message and frame_opcode != 0:
514
+ self.protocolViolation("received non-continuation data frame while inside fragmented message")
515
+ return False
516
+
517
+ ## compute complete header length
518
+ ##
519
+ if frame_masked:
520
+ mask_len = 4
521
+ else:
522
+ mask_len = 0
523
+
524
+ if frame_payload_len1 < 126:
525
+ frame_header_len = 2 + mask_len
526
+ elif frame_payload_len1 == 126:
527
+ frame_header_len = 2 + 2 + mask_len
528
+ elif frame_payload_len1 == 127:
529
+ frame_header_len = 2 + 8 + mask_len
530
+ else:
531
+ raise Exception("logic error")
532
+
533
+ ## only proceed when we have enough data buffered for complete
534
+ ## frame header (which includes extended payload len + mask)
535
+ ##
536
+ if buffered_len >= frame_header_len:
537
+
538
+ i = 2
539
+
540
+ ## extract extended payload length
541
+ ##
542
+ if frame_payload_len1 == 126:
543
+ frame_payload_len = struct.unpack("!H", self.data[i:i+2])[0]
544
+ i += 2
545
+ elif frame_payload_len1 == 127:
546
+ frame_payload_len = struct.unpack("!Q", self.data[i:i+8])[0]
547
+ if frame_payload_len > 0x7FFFFFFFFFFFFFFF: # 2**63
548
+ self.protocolViolation("invalid data frame length (>2^63)")
549
+ return False
550
+ i += 8
551
+ else:
552
+ frame_payload_len = frame_payload_len1
553
+
554
+ ## when payload is masked, extract frame mask
555
+ ##
556
+ frame_mask = None
557
+ frame_mask_array = []
558
+ if frame_masked:
559
+ frame_mask = self.data[i:i+4]
560
+ for j in range(0, 4):
561
+ frame_mask_array.append(ord(frame_mask[j]))
562
+ i += 4
563
+
564
+ ## remember rest (payload of current frame after header and everything thereafter)
565
+ ##
566
+ self.data = self.data[i:]
567
+
568
+ ## ok, got complete frame header
569
+ ##
570
+ self.current_frame = FrameHeader(frame_opcode, frame_fin, frame_rsv, frame_payload_len, frame_mask, frame_mask_array)
571
+
572
+ ## process begin on new frame
573
+ ##
574
+ self.onFrameBegin()
575
+
576
+ ## reprocess when frame has no payload or and buffered data left
577
+ ##
578
+ return frame_payload_len == 0 or len(self.data) > 0
579
+
580
+ else:
581
+ return False # need more data
582
+ else:
583
+ return False # need more data
584
+
585
+ ## inside a started frame
586
+ ##
587
+ else:
588
+
589
+ ## cut out rest of frame payload
590
+ ##
591
+ rest = self.current_frame.length - self.current_frame.ptr
592
+ if buffered_len >= rest:
593
+ payload = bytearray(self.data[:rest])
594
+ length = rest
595
+ self.data = self.data[rest:]
596
+ else:
597
+ payload = bytearray(self.data)
598
+ length = buffered_len
599
+ self.data = ""
600
+
601
+ if length > 0:
602
+ ## unmask payload
603
+ ##
604
+ if self.current_frame.mask:
605
+ for k in xrange(0, length):
606
+ payload[k] ^= self.current_frame.mask_array[(k + self.current_frame.ptr) % 4]
607
+
608
+ ## process frame data
609
+ ##
610
+ self.onFrameData(payload)
611
+
612
+ ## advance payload pointer and fire frame end handler when frame payload is complete
613
+ ##
614
+ self.current_frame.ptr += length
615
+ if self.current_frame.ptr == self.current_frame.length:
616
+ self.onFrameEnd()
617
+
618
+ ## reprocess when no error occurred and buffered data left
619
+ ##
620
+ return len(self.data) > 0
621
+
622
+
623
+ def onFrameBegin(self):
624
+ if self.current_frame.opcode > 7:
625
+ self.control_frame_data = bytearray()
626
+ else:
627
+ if not self.inside_message:
628
+ self.inside_message = True
629
+ self.onMessageBegin(self.current_frame.opcode)
630
+ self.onMessageFrameBegin(self.current_frame.length, self.current_frame.rsv)
631
+
632
+
633
+ def onFrameData(self, payload):
634
+ if self.current_frame.opcode > 7:
635
+ self.control_frame_data.extend(payload)
636
+ else:
637
+ self.onMessageFrameData(payload)
638
+
639
+
640
+ def onFrameEnd(self):
641
+ if self.current_frame.opcode > 7:
642
+ self.processControlFrame()
643
+ else:
644
+ self.onMessageFrameEnd()
645
+ if self.current_frame.fin:
646
+ self.onMessageEnd()
647
+ self.inside_message = False
648
+ self.current_frame = None
649
+
650
+
651
+ def processControlFrame(self):
652
+ payload = str(self.control_frame_data)
653
+ self.control_frame_data = None
654
+
655
+ self.logRxFrame(self.current_frame.fin, self.current_frame.rsv, self.current_frame.opcode, self.current_frame.mask is not None, self.current_frame.length, self.current_frame.mask, payload)
656
+
657
+ ## CLOSE frame
658
+ ##
659
+ if self.current_frame.opcode == 8:
660
+
661
+ code = None
662
+ reason = None
663
+
664
+ plen = len(payload)
665
+ if plen > 0:
666
+
667
+ ## If there is a body, the first two bytes of the body MUST be a 2-byte
668
+ ## unsigned integer (in network byte order) representing a status code
669
+ ##
670
+ code = struct.unpack("!H", payload[0:2])[0]
671
+
672
+ ## Following the 2-byte integer the body MAY contain UTF-8
673
+ ## encoded data with value /reason/, the interpretation of which is not
674
+ ## defined by this specification.
675
+ ##
676
+ if plen > 2:
677
+ try:
678
+ reason = unicode(payload[2:], 'utf8')
679
+ except UnicodeDecodeError:
680
+ self.protocolViolation("received non-UTF-8 payload as close frame reason")
681
+ return False
682
+
683
+ self.onClose(code, reason)
684
+
685
+ ## PING frame
686
+ ##
687
+ elif self.current_frame.opcode == 9:
688
+ self.onPing(payload)
689
+
690
+ ## PONG frame
691
+ ##
692
+ elif self.current_frame.opcode == 10:
693
+ self.onPong(payload)
694
+
695
+ else:
696
+ raise Exception("logic error")
697
+
698
+ return True
699
+
700
+
701
+ def sendFrame(self, opcode, payload = "", fin = True, rsv = 0, mask = None, payload_len = None, chopsize = None, sync = False):
702
+ """
703
+ Send out frame. Normally only used internally via sendMessage(), sendPing(), sendPong() and sendClose().
704
+
705
+ This method deliberately allows to send invalid frames (that is frames invalid
706
+ per-se, or frames invalid because of protocol state). Other than in fuzzing servers,
707
+ calling methods will ensure that no invalid frames are sent.
708
+
709
+ In addition, this method supports explicit specification of payload length.
710
+ When payload_len is given, it will always write that many octets to the stream.
711
+ It'll wrap within payload, resending parts of that when more octets were requested
712
+ The use case is again for fuzzing server which want to sent increasing amounts
713
+ of payload data to peers without having to construct potentially large messges
714
+ themselfes.
715
+ """
716
+ if payload_len:
717
+ if payload_len < 1 or len(payload) < 1:
718
+ raise Exception("cannot construct repeated payload with length %d from payload of length %d" % (payload_len, len(payload)))
719
+ l = payload_len
720
+ pl = ''.join([payload for k in range(payload_len / len(payload))]) + payload[:payload_len % len(payload)]
721
+ else:
722
+ l = len(payload)
723
+ pl = payload
724
+
725
+ ## first byte
726
+ ##
727
+ b0 = 0
728
+ if fin:
729
+ b0 |= (1 << 7)
730
+ b0 |= (rsv % 8) << 4
731
+ b0 |= opcode % 128
732
+
733
+ ## second byte, payload len bytes and mask
734
+ ##
735
+ b1 = 0
736
+ el = ""
737
+ if mask or not self.isServer:
738
+ b1 |= 1 << 7
739
+ if mask:
740
+ mv = struct.pack("!I", mask)
741
+ else:
742
+ mv = struct.pack("!I", random.getrandbits(32))
743
+
744
+ frame_mask = []
745
+ for j in range(0, 4):
746
+ frame_mask.append(ord(mv[j]))
747
+
748
+ ## mask frame payload
749
+ ##
750
+ pl_ba = bytearray(pl)
751
+ for k in xrange(0, l):
752
+ pl_ba[k] ^= frame_mask[k % 4]
753
+ plm = str(pl_ba)
754
+
755
+ else:
756
+ mv = ""
757
+ plm = pl
758
+
759
+ if l <= 125:
760
+ b1 |= l
761
+ elif l <= 0xFFFF:
762
+ b1 |= 126
763
+ el = struct.pack("!H", l)
764
+ elif l <= 0x7FFFFFFFFFFFFFFF:
765
+ b1 |= 127
766
+ el = struct.pack("!Q", l)
767
+ else:
768
+ raise Exception("invalid payload length")
769
+
770
+ raw = ''.join([chr(b0), chr(b1), el, mv, plm])
771
+
772
+ ## log frame TX
773
+ ##
774
+ if mv != "":
775
+ mmv = binascii.b2a_hex(mv)
776
+ else:
777
+ mmv = None
778
+ self.logTxFrame(opcode, payload, fin, rsv, mmv, payload_len, chopsize, sync)
779
+
780
+ ## send frame octets
781
+ ##
782
+ self.sendData(raw, sync, chopsize)
783
+
784
+
785
+ def sendPing(self, payload = None):
786
+ """
787
+ Send out Ping to peer. A peer is expected to Pong back the payload a soon
788
+ as "practical". When more than 1 Ping is outstanding at a peer, the peer may
789
+ elect to respond only to the last Ping.
790
+
791
+ :param payload: An optional, arbitrary payload of length < 126 octets.
792
+ :type payload: str
793
+ """
794
+ if payload:
795
+ l = len(payload)
796
+ if l > 125:
797
+ raise Exception("invalid payload for PING (payload length must be <= 125, was %d)" % l)
798
+ self.sendFrame(opcode = 9, payload = payload)
799
+ else:
800
+ self.sendFrame(opcode = 9)
801
+
802
+
803
+ def sendPong(self, payload = None):
804
+ """
805
+ Send out Pong to peer. A Pong frame MAY be sent unsolicited.
806
+ This serves as a unidirectional heartbeat. A response to an unsolicited pong is "not expected".
807
+
808
+ :param payload: An optional, arbitrary payload of length < 126 octets.
809
+ :type payload: str
810
+ """
811
+ if payload:
812
+ l = len(payload)
813
+ if l > 125:
814
+ raise Exception("invalid payload for PONG (payload length must be <= 125, was %d)" % l)
815
+ self.sendFrame(opcode = 10, payload = payload)
816
+ else:
817
+ self.sendFrame(opcode = 10)
818
+
819
+
820
+ def sendClose(self, code = None, reason = None):
821
+ """Send close to WebSockets peer.
822
+
823
+ :param code: An optional close status code (:class:`WebSocketProtocol`.CLOSE_STATUS_CODE_*).
824
+ :type code: int
825
+ :param reason: An optional close reason (when present, a status code MUST also be present).
826
+ :type reason: str
827
+ """
828
+ plen = 0
829
+ payload = ""
830
+
831
+ if code is not None:
832
+
833
+ if (not (code >= 3000 and code <= 3999)) and \
834
+ (not (code >= 4000 and code <= 4999)) and \
835
+ code not in [WebSocketProtocol.CLOSE_STATUS_CODE_NORMAL,
836
+ WebSocketProtocol.CLOSE_STATUS_CODE_GOING_AWAY,
837
+ WebSocketProtocol.CLOSE_STATUS_CODE_PROTOCOL_ERROR,
838
+ WebSocketProtocol.CLOSE_STATUS_CODE_PAYLOAD_NOT_ACCEPTED,
839
+ WebSocketProtocol.CLOSE_STATUS_CODE_FRAME_TOO_LARGE,
840
+ WebSocketProtocol.CLOSE_STATUS_CODE_TEXT_FRAME_NOT_UTF8]:
841
+ raise Exception("invalid status code %d for close frame" % code)
842
+
843
+ payload = struct.pack("!H", code)
844
+ plen = 2
845
+
846
+ if reason is not None:
847
+ reason = reason.encode("UTF-8")
848
+ plen += len(reason)
849
+ else:
850
+ reason = ""
851
+
852
+ if plen > 125:
853
+ raise Exception("close frame payload larger than 125 octets")
854
+
855
+ payload += reason
856
+
857
+ else:
858
+ if reason is not None and reason != "":
859
+ raise Exception("status reason '%s' without status code in close frame" % reason)
860
+
861
+ self.sendFrame(opcode = 8, payload = payload)
862
+ self.closeAlreadySent = True
863
+
864
+
865
+ def beginMessage(self, opcode = MESSAGE_TYPE_TEXT):
866
+ """
867
+ Begin sending new message.
868
+
869
+ :param opcode: Message type, normally either WebSocketProtocol.MESSAGE_TYPE_TEXT (default) or WebSocketProtocol.MESSAGE_TYPE_BINARY.
870
+ """
871
+ ## check if sending state is valid for this method
872
+ ##
873
+ if self.send_state != WebSocketProtocol.SEND_STATE_GROUND:
874
+ raise Exception("WebSocketProtocol.beginMessage invalid in current sending state")
875
+
876
+ if opcode not in [1, 2]:
877
+ raise Exception("use of reserved opcode %d" % opcode)
878
+
879
+ ## move into "begin message" state and remember opcode for later (when sending first frame)
880
+ ##
881
+ self.send_state = WebSocketProtocol.SEND_STATE_MESSAGE_BEGIN
882
+ self.send_message_opcode = opcode
883
+
884
+
885
+ def beginMessageFrame(self, length, reserved = 0, mask = None):
886
+ """
887
+ Begin sending new message frame.
888
+
889
+ :param length: Length of frame which is started. Must be >= 0 and <= 2^63.
890
+ :type length: int
891
+ :param reserved: Reserved bits for frame (an integer from 0 to 7). Note that reserved != 0 is only legal when an extension has been negoiated which defines semantics.
892
+ :type reserved: int
893
+ :param mask: Optional frame mask. When given, this is used. When None and the peer is a client, a mask will be internally generated. For servers None is default.
894
+ :type mask: str
895
+ """
896
+ ## check if sending state is valid for this method
897
+ ##
898
+ if self.send_state not in [WebSocketProtocol.SEND_STATE_MESSAGE_BEGIN, WebSocketProtocol.SEND_STATE_INSIDE_MESSAGE]:
899
+ raise Exception("WebSocketProtocol.beginMessageFrame invalid in current sending state")
900
+
901
+ if (not type(length) in [int, long]) or length < 0 or length > 0x7FFFFFFFFFFFFFFF: # 2**63
902
+ raise Exception("invalid value for message frame length")
903
+
904
+ if type(reserved) is not int or reserved < 0 or reserved > 7:
905
+ raise Exception("invalid value for reserved bits")
906
+
907
+ self.send_message_frame_length = length
908
+ self.send_message_frame_octets_sent = 0
909
+
910
+ if mask:
911
+ if type(mask) is not str:
912
+ raise Exception("mask must be a (byte) string")
913
+ if len(mask) != 4:
914
+ raise Exception("mask must have length 4")
915
+ self.send_message_frame_mask = mask
916
+ elif not self.isServer:
917
+ self.send_message_frame_mask = random.getrandbits(32)
918
+ else:
919
+ self.send_message_frame_mask = None
920
+
921
+ ## first byte
922
+ ##
923
+ b0 = (reserved % 8) << 4 # FIN = false .. since with streaming, we don't know when message ends
924
+
925
+ if self.send_state == WebSocketProtocol.SEND_STATE_MESSAGE_BEGIN:
926
+ self.send_state = WebSocketProtocol.SEND_STATE_INSIDE_MESSAGE
927
+ b0 |= self.send_message_opcode % 128
928
+ else:
929
+ pass # message continuation frame
930
+
931
+ ## second byte, payload len bytes and mask
932
+ ##
933
+ b1 = 0
934
+ el = ""
935
+ if self.send_message_frame_mask:
936
+ b1 |= 1 << 7
937
+ mv = struct.pack("!I", self.send_message_frame_mask)
938
+ self.send_message_frame_mask_array = []
939
+ for j in range(0, 4):
940
+ self.send_message_frame_mask_array.append(ord(mv[j]))
941
+ else:
942
+ mv = ""
943
+
944
+ if length <= 125:
945
+ b1 |= length
946
+ elif length <= 0xFFFF:
947
+ b1 |= 126
948
+ el = struct.pack("!H", length)
949
+ elif length <= 0x7FFFFFFFFFFFFFFF:
950
+ b1 |= 127
951
+ el = struct.pack("!Q", length)
952
+ else:
953
+ raise Exception("invalid payload length")
954
+
955
+ ## write message frame header
956
+ ##
957
+ header = ''.join([chr(b0), chr(b1), el, mv])
958
+ self.transport.write(header)
959
+
960
+ ## now we are inside message frame ..
961
+ ##
962
+ self.send_state = WebSocketProtocol.SEND_STATE_INSIDE_MESSAGE_FRAME
963
+
964
+
965
+ def sendMessageFrameData(self, payload):
966
+ """
967
+ Send out data when within message frame (message was begun, frame was begun).
968
+ Note that the frame is automatically ended when enough data has been sent
969
+ that is, there is no endMessageFrame, since you have begun the frame specifying
970
+ the frame length, which implicitly defined the frame end. This is different from
971
+ messages, which you begin and end, since a message can contain an unlimited number
972
+ of frames.
973
+
974
+ :param payload: Data to send.
975
+ :returns: int -- When frame still incomplete, returns outstanding octets, when frame complete, returns <= 0, when < 0, the amount of unconsumed data in payload argument.
976
+ """
977
+ ## check if sending state is valid for this method
978
+ ##
979
+ if self.send_state != WebSocketProtocol.SEND_STATE_INSIDE_MESSAGE_FRAME:
980
+ raise Exception("WebSocketProtocol.sendMessageFrameData invalid in current sending state")
981
+
982
+ rl = len(payload)
983
+ if self.send_message_frame_octets_sent + rl > self.send_message_frame_length:
984
+ l = self.send_message_frame_length - self.send_message_frame_octets_sent
985
+ rest = -(rl - l)
986
+ pl_ba = bytearray(payload[:l])
987
+ else:
988
+ l = rl
989
+ rest = self.send_message_frame_length - self.send_message_frame_octets_sent - l
990
+ pl_ba = bytearray(payload)
991
+
992
+ ## mask frame payload
993
+ ##
994
+ if self.send_message_frame_mask:
995
+ w = self.send_message_frame_octets_sent % 4
996
+ for k in xrange(0, l):
997
+ pl_ba[k] ^= self.send_message_frame_mask_array[(w + k) % 4]
998
+ ## WARNING. This is silly: if you do it instead like the uncommended line, the memory footprint
999
+ ## will run away ...
1000
+ # pl_ba[k] ^= self.send_message_frame_mask_array[(self.send_message_frame_octets_sent + k) % 4]
1001
+
1002
+ ## send frame payload
1003
+ ##
1004
+ self.transport.write(str(pl_ba))
1005
+
1006
+ pl_ba = None
1007
+
1008
+ #if sync:
1009
+ # self.syncSocket()
1010
+
1011
+ ## advance frame payload pointer and check if frame payload was completely sent
1012
+ ##
1013
+ self.send_message_frame_octets_sent += l
1014
+
1015
+ ## if we are done with frame, move back into "inside message" state
1016
+ ##
1017
+ if self.send_message_frame_octets_sent >= self.send_message_frame_length:
1018
+ self.send_state = WebSocketProtocol.SEND_STATE_INSIDE_MESSAGE
1019
+
1020
+ ## when =0 : frame was completed exactly
1021
+ ## when >0 : frame is still uncomplete and that much amount is still left to complete the frame
1022
+ ## when <0 : frame was completed and there was this much unconsumed data in payload argument
1023
+ ##
1024
+ return rest
1025
+
1026
+
1027
+ def endMessage(self):
1028
+ """
1029
+ End a previously begun message. No more frames may be sent (for that message). You have to
1030
+ begin a new message before sending again.
1031
+ """
1032
+ ## check if sending state is valid for this method
1033
+ ##
1034
+ if self.send_state != WebSocketProtocol.SEND_STATE_INSIDE_MESSAGE:
1035
+ raise Exception("WebSocketProtocol.endMessage invalid in current sending state [%d]" % self.send_state)
1036
+ self.sendFrame(opcode = 0, fin = True)
1037
+ self.send_state = WebSocketProtocol.SEND_STATE_GROUND
1038
+
1039
+
1040
+ def sendMessageFrame(self, payload, reserved = 0, mask = None, sync = False):
1041
+ """
1042
+ When a message has begun, send a complete message frame in one go.
1043
+ """
1044
+ self.beginMessageFrame(len(payload), reserved, mask)
1045
+ self.sendMessageFrameData(payload, sync)
1046
+
1047
+
1048
+ def sendMessage(self, payload, binary = False, payload_frag_size = None, sync = False):
1049
+ """
1050
+ Send out a message in one go.
1051
+
1052
+ You can send text or binary message, and optionally
1053
+ specifiy a payload fragment size. When the latter is given, the payload will
1054
+ be split up into frames with payload <= the payload_frag_size given.
1055
+ """
1056
+ ## (initial) frame opcode
1057
+ ##
1058
+ if binary:
1059
+ opcode = 2
1060
+ else:
1061
+ opcode = 1
1062
+
1063
+ ## send unfragmented
1064
+ ##
1065
+ if payload_frag_size is None or len(payload) <= payload_frag_size:
1066
+ self.sendFrame(opcode = opcode, payload = payload, sync = sync)
1067
+
1068
+ ## send data message in fragments
1069
+ ##
1070
+ else:
1071
+ if payload_frag_size < 1:
1072
+ raise Exception("payload fragment size must be at least 1 (was %d)" % payload_frag_size)
1073
+ i = 0
1074
+ n = len(payload)
1075
+ done = False
1076
+ first = True
1077
+ while not done:
1078
+ j = i + payload_frag_size
1079
+ if j > n:
1080
+ done = True
1081
+ j = n
1082
+ if first:
1083
+ self.sendFrame(opcode = opcode, payload = payload[i:j], fin = done, sync = sync)
1084
+ first = False
1085
+ else:
1086
+ self.sendFrame(opcode = 0, payload = payload[i:j], fin = done, sync = sync)
1087
+ i += payload_frag_size
1088
+
1089
+
1090
+ class HttpException():
1091
+ def __init__(self, code, reason):
1092
+ self.code = code
1093
+ self.reason = reason
1094
+
1095
+
1096
+ class WebSocketServerProtocol(WebSocketProtocol):
1097
+ """
1098
+ A Twisted protocol for WebSockets servers.
1099
+ """
1100
+
1101
+ def onConnect(self, host, path, params, origin, protocols):
1102
+ """
1103
+ Callback when new WebSocket client connection is established.
1104
+ Throw HttpException when you don't want to accept WebSocket connection.
1105
+ Return accepted protocol from list of protocols provided by client or None.
1106
+
1107
+ Override in derived class.
1108
+ """
1109
+ return None
1110
+
1111
+ def connectionMade(self):
1112
+ WebSocketProtocol.connectionMade(self)
1113
+ if self.debug:
1114
+ log.msg("WebSocketServiceConnection.connectionMade")
1115
+ log.msg("connection accepted from %s" % self.peerstr)
1116
+ self.http_request = None
1117
+ self.http_headers = {}
1118
+ self.isServer = True
1119
+
1120
+
1121
+ def connectionLost(self, reason):
1122
+ WebSocketProtocol.connectionLost(self, reason)
1123
+ if self.debug:
1124
+ log.msg("WebSocketServiceConnection.connectionLost")
1125
+ log.msg("connection from %s lost" % self.peerstr)
1126
+
1127
+
1128
+ def processHandshake(self):
1129
+ """
1130
+ Process WebSockets handshake.
1131
+ """
1132
+ ## only proceed when we have fully received the HTTP request line and all headers
1133
+ ##
1134
+ end_of_header = self.data.find("\x0d\x0a\x0d\x0a")
1135
+ if end_of_header >= 0:
1136
+
1137
+ ## extract HTTP headers
1138
+ ##
1139
+ ## FIXME: properly handle headers split accross multiple lines
1140
+ ##
1141
+ raw = self.data[:end_of_header].splitlines()
1142
+ self.http_request = raw[0].strip()
1143
+ for h in raw[1:]:
1144
+ i = h.find(":")
1145
+ if i > 0:
1146
+ key = h[:i].strip()
1147
+ value = h[i+1:].strip()
1148
+ self.http_headers[key] = value
1149
+
1150
+ ## remember rest (after HTTP headers, if any)
1151
+ ##
1152
+ self.data = self.data[end_of_header + 4:]
1153
+
1154
+ ## self.http_request & self.http_headers are now set
1155
+ ## => validate WebSocket handshake
1156
+ ##
1157
+
1158
+ if self.debug:
1159
+ log.msg("received request line in handshake : %s" % str(self.http_request))
1160
+ log.msg("received headers in handshake : %s" % str(self.http_headers))
1161
+
1162
+ ## HTTP Request line : METHOD, VERSION
1163
+ ##
1164
+ rl = self.http_request.split(" ")
1165
+ if len(rl) != 3:
1166
+ return self.sendHttpBadRequest("bad HTTP request line '%s'" % self.http_request)
1167
+ if rl[0] != "GET":
1168
+ return self.sendHttpBadRequest("illegal HTTP method '%s'" % rl[0])
1169
+ vs = rl[2].split("/")
1170
+ if len(vs) != 2 or vs[0] != "HTTP" or vs[1] not in ["1.1"]:
1171
+ return self.sendHttpBadRequest("bad HTTP version '%s'" % rl[2])
1172
+
1173
+ ## HTTP Request line : REQUEST-URI
1174
+ ##
1175
+ ## FIXME: checking
1176
+ ##
1177
+ self.http_request_uri = rl[1]
1178
+ (scheme, loc, path, params, query, fragment) = urlparse.urlparse(self.http_request_uri)
1179
+ if fragment != "":
1180
+ return self.sendHttpBadRequest("HTTP Request URI with fragment identifier")
1181
+ self.http_request_path = path
1182
+ self.http_request_params = urlparse.parse_qs(query)
1183
+
1184
+ ## Host
1185
+ ##
1186
+ ## FIXME: checking
1187
+ ##
1188
+ if not self.http_headers.has_key("Host"):
1189
+ return self.sendHttpBadRequest("HTTP Host header missing")
1190
+ self.http_request_host = self.http_headers["Host"].strip()
1191
+
1192
+ ## Upgrade
1193
+ ##
1194
+ if not self.http_headers.has_key("Upgrade"):
1195
+ return self.sendHttpBadRequest("HTTP Upgrade header missing")
1196
+ if self.http_headers["Upgrade"] != "websocket":
1197
+ return self.sendHttpBadRequest("HTTP Upgrade header different from 'websocket'")
1198
+
1199
+ ## Connection
1200
+ ##
1201
+ if not self.http_headers.has_key("Connection"):
1202
+ return self.sendHttpBadRequest("HTTP Connection header missing")
1203
+ connectionUpgrade = False
1204
+ for c in self.http_headers["Connection"].split(","):
1205
+ if c.strip() == "Upgrade":
1206
+ connectionUpgrade = True
1207
+ break
1208
+ if not connectionUpgrade:
1209
+ return self.sendHttpBadRequest("HTTP Connection header does not include 'Upgrade' value")
1210
+
1211
+ ## Sec-WebSocket-Version
1212
+ ##
1213
+ if not self.http_headers.has_key("Sec-WebSocket-Version"):
1214
+ return self.sendHttpBadRequest("HTTP Sec-WebSocket-Version header missing")
1215
+ try:
1216
+ version = int(self.http_headers["Sec-WebSocket-Version"])
1217
+ if version < 8:
1218
+ return self.sendHttpBadRequest("Sec-WebSocket-Version %d not supported (only >= 8)" % version)
1219
+ else:
1220
+ self.websocket_version = version
1221
+ except:
1222
+ return self.sendHttpBadRequest("could not parse HTTP Sec-WebSocket-Version header '%s'" % self.http_headers["Sec-WebSocket-Version"])
1223
+
1224
+ ## Sec-WebSocket-Protocol
1225
+ ##
1226
+ ##
1227
+ if self.http_headers.has_key("Sec-WebSocket-Protocol"):
1228
+ protocols = self.http_headers["Sec-WebSocket-Protocol"].split(",")
1229
+ # check for duplicates in protocol header
1230
+ pp = {}
1231
+ for p in protocols:
1232
+ if pp.has_key(p):
1233
+ return self.sendHttpBadRequest("duplicate protocol '%s' specified in HTTP Sec-WebSocket-Protocol header" % p)
1234
+ else:
1235
+ pp[p] = 1
1236
+ # ok, no duplicates, save list in order the client sent it
1237
+ self.websocket_protocols = protocols
1238
+ else:
1239
+ self.websocket_protocols = []
1240
+
1241
+ ## Sec-WebSocket-Origin
1242
+ ## http://tools.ietf.org/html/draft-ietf-websec-origin-02
1243
+ ##
1244
+ ## FIXME: checking
1245
+ ##
1246
+ if self.http_headers.has_key("Sec-WebSocket-Origin"):
1247
+ origin = self.http_headers["Sec-WebSocket-Origin"].strip()
1248
+ if origin == "null":
1249
+ self.websocket_origin = None
1250
+ else:
1251
+ self.websocket_origin = origin
1252
+ else:
1253
+ self.websocket_origin = None
1254
+
1255
+ ## Sec-WebSocket-Extensions
1256
+ ##
1257
+ if self.http_headers.has_key("Sec-WebSocket-Extensions"):
1258
+ pass
1259
+
1260
+ ## Sec-WebSocket-Key
1261
+ ## http://tools.ietf.org/html/rfc4648#section-4
1262
+ ##
1263
+ if not self.http_headers.has_key("Sec-WebSocket-Key"):
1264
+ return self.sendHttpBadRequest("HTTP Sec-WebSocket-Version header missing")
1265
+ key = self.http_headers["Sec-WebSocket-Key"].strip()
1266
+ if len(key) != 24: # 16 bytes => (ceil(128/24)*24)/6 == 24
1267
+ return self.sendHttpBadRequest("bad Sec-WebSocket-Key (length must be 24 ASCII chars) '%s'" % key)
1268
+ if key[-2:] != "==": # 24 - ceil(128/6) == 2
1269
+ return self.sendHttpBadRequest("bad Sec-WebSocket-Key (invalid base64 encoding) '%s'" % key)
1270
+ for c in key[:-2]:
1271
+ if c not in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/":
1272
+ return self.sendHttpBadRequest("bad character '%s' in Sec-WebSocket-Key (invalid base64 encoding) '%s'" (c, key))
1273
+
1274
+ ## WebSocket handshake validated
1275
+ ## => produce response
1276
+
1277
+ ## Now fire onConnect() on derived class, to give that class a chance to accept or deny
1278
+ ## the connection. onConnect() may throw, in which case the connection is denied, or it
1279
+ ## may return a protocol from the protocols provided by client or None.
1280
+ ##
1281
+ try:
1282
+ protocol = self.onConnect(self.http_request_host, self.http_request_path, self.http_request_params, self.websocket_origin, self.websocket_protocols)
1283
+ if protocol and not (protocol in self.websocket_protocols):
1284
+ raise Exception("protocol accepted must be from the list client sent or null")
1285
+ except HttpException, e:
1286
+ return self.sendHttpRequestFailure(e.code, e.reason)
1287
+
1288
+ ## compute Sec-WebSocket-Accept
1289
+ ##
1290
+ sha1 = hashlib.sha1()
1291
+ sha1.update(key + WebSocketProtocol.WS_MAGIC)
1292
+ sec_websocket_accept = base64.b64encode(sha1.digest())
1293
+
1294
+ ## send response to complete WebSocket handshake
1295
+ ##
1296
+ response = "HTTP/1.1 101 Switching Protocols\x0d\x0a"
1297
+ response += "Upgrade: websocket\x0d\x0a"
1298
+ response += "Connection: Upgrade\x0d\x0a"
1299
+ response += "Sec-WebSocket-Accept: %s\x0d\x0a" % sec_websocket_accept
1300
+ if protocol:
1301
+ response += "Sec-WebSocket-Protocol: %s\x0d\x0a" % protocol
1302
+ response += "\x0d\x0a"
1303
+
1304
+ if self.debug:
1305
+ log.msg("send handshake : %s" % response)
1306
+ self.sendData(response)
1307
+
1308
+ ## move into OPEN state
1309
+ ##
1310
+ self.state = WebSocketProtocol.STATE_OPEN
1311
+ self.current_frame = None
1312
+ self.inside_message = False
1313
+
1314
+ ## fire handler on derived class
1315
+ ##
1316
+ self.onOpen()
1317
+
1318
+ ## process rest, if any
1319
+ ##
1320
+ if len(self.data) > 0:
1321
+ self.consumeData()
1322
+
1323
+
1324
+ def sendHttpBadRequest(self, reason):
1325
+ """
1326
+ When problems/errors happen during WebSockets handshake, send HTTP
1327
+ Bad Request Error and terminate.
1328
+ """
1329
+ self.sendHttpRequestFailure(400, reason)
1330
+
1331
+
1332
+ def sendHttpRequestFailure(self, code, reason):
1333
+ """
1334
+ Send out HTTP error.
1335
+ """
1336
+ response = "HTTP/1.1 %d %s\x0d\x0a" % (code, reason)
1337
+ response += "\x0d\x0a"
1338
+ if self.debug:
1339
+ log.msg("send handshake failure : %s" % response)
1340
+ self.sendData(response)
1341
+ self.transport.loseConnection()
1342
+
1343
+
1344
+ class WebSocketServerFactory(protocol.ServerFactory):
1345
+ """
1346
+ A Twisted factory for WebSockets server protocols.
1347
+ """
1348
+
1349
+ protocol = WebSocketServerProtocol
1350
+
1351
+ def __init__(self, debug = False):
1352
+ self.debug = debug
1353
+
1354
+ def startFactory(self):
1355
+ pass
1356
+
1357
+ def stopFactory(self):
1358
+ pass
1359
+
1360
+
1361
+ class WebSocketClientProtocol(WebSocketProtocol):
1362
+ """
1363
+ Client protocol for WebSockets.
1364
+ """
1365
+
1366
+ def __init__(self, debug = False):
1367
+ self.debug = debug
1368
+
1369
+
1370
+ def connectionMade(self):
1371
+ WebSocketProtocol.connectionMade(self)
1372
+ if self.debug:
1373
+ log.msg("connection to %s established" % self.peerstr)
1374
+ self.http_request = None
1375
+ self.http_headers = {}
1376
+ self.isServer = False
1377
+ self.startHandshake()
1378
+
1379
+
1380
+ def connectionLost(self, reason):
1381
+ WebSocketProtocol.connectionLost(self, reason)
1382
+ if self.debug:
1383
+ log.msg("connection to %s lost" % self.peerstr)
1384
+
1385
+
1386
+ def startHandshake(self):
1387
+ self.websocket_key = base64.b64encode(os.urandom(16))
1388
+
1389
+ request = "GET %s HTTP/1.1\x0d\x0a" % self.factory.path
1390
+ request += "Host: localhost:9000\x0d\x0a"
1391
+ request += "Upgrade: websocket\x0d\x0a"
1392
+ request += "Connection: Upgrade\x0d\x0a"
1393
+ request += "Sec-WebSocket-Key: %s\x0d\x0a" % self.websocket_key
1394
+ request += "Sec-WebSocket-Version: 8\x0d\x0a"
1395
+ request += "\x0d\x0a"
1396
+
1397
+ if self.debug:
1398
+ log.msg(request)
1399
+
1400
+ self.sendData(request)
1401
+
1402
+
1403
+ def processHandshake(self):
1404
+ """
1405
+ Process WebSockets handshake.
1406
+ """
1407
+ ## only proceed when we have fully received the HTTP request line and all headers
1408
+ ##
1409
+ end_of_header = self.data.find("\x0d\x0a\x0d\x0a")
1410
+ if end_of_header >= 0:
1411
+
1412
+ ## extract HTTP headers
1413
+ ##
1414
+ ## FIXME: properly handle headers split accross multiple lines
1415
+ ##
1416
+ raw = self.data[:end_of_header].splitlines()
1417
+ self.http_request = raw[0].strip()
1418
+ for h in raw[1:]:
1419
+ i = h.find(":")
1420
+ if i > 0:
1421
+ key = h[:i].strip()
1422
+ value = h[i+1:].strip()
1423
+ self.http_headers[key] = value
1424
+
1425
+ ## remember rest (after HTTP headers, if any)
1426
+ ##
1427
+ self.data = self.data[end_of_header + 4:]
1428
+
1429
+ ## self.http_request & self.http_headers are now set
1430
+ ## => validate WebSocket handshake
1431
+ ##
1432
+
1433
+ if self.debug:
1434
+ log.msg("received request line in handshake : %s" % str(self.http_request))
1435
+ log.msg("received headers in handshake : %s" % str(self.http_headers))
1436
+
1437
+ ## Response Line
1438
+ ##
1439
+ if self.http_request != "HTTP/1.1 101 Switching Protocols":
1440
+ pass
1441
+
1442
+ ## Upgrade
1443
+ ##
1444
+ if not self.http_headers.has_key("Upgrade"):
1445
+ return self.sendHttpBadRequest("HTTP Upgrade header missing")
1446
+ if self.http_headers["Upgrade"] != "websocket":
1447
+ return self.sendHttpBadRequest("HTTP Upgrade header different from 'websocket'")
1448
+
1449
+ ## Connection
1450
+ ##
1451
+ if not self.http_headers.has_key("Connection"):
1452
+ return self.sendHttpBadRequest("HTTP Connection header missing")
1453
+ connectionUpgrade = False
1454
+ for c in self.http_headers["Connection"].split(","):
1455
+ if c.strip() == "Upgrade":
1456
+ connectionUpgrade = True
1457
+ break
1458
+ if not connectionUpgrade:
1459
+ return self.sendHttpBadRequest("HTTP Connection header does not include 'Upgrade' value")
1460
+
1461
+ ## compute Sec-WebSocket-Accept
1462
+ ##
1463
+ if not self.http_headers.has_key("Sec-WebSocket-Accept"):
1464
+ return self.sendHttpBadRequest("HTTP Sec-WebSocket-Accept header missing")
1465
+ else:
1466
+ sha1 = hashlib.sha1()
1467
+ sha1.update(self.websocket_key + WebSocketProtocol.WS_MAGIC)
1468
+ sec_websocket_accept = base64.b64encode(sha1.digest())
1469
+
1470
+ if self.http_headers["Sec-WebSocket-Accept"] != sec_websocket_accept:
1471
+ pass
1472
+
1473
+ ## move into OPEN state
1474
+ ##
1475
+ self.state = WebSocketProtocol.STATE_OPEN
1476
+ self.current_frame = None
1477
+ self.inside_message = False
1478
+
1479
+ ## fire handler on derived class
1480
+ ##
1481
+ self.onOpen()
1482
+
1483
+ ## process rest, if any
1484
+ ##
1485
+ if len(self.data) > 0:
1486
+ self.consumeData()
1487
+
1488
+
1489
+ class WebSocketClientFactory(protocol.ClientFactory):
1490
+ """
1491
+ Client factory for WebSockets.
1492
+ """
1493
+
1494
+ protocol = WebSocketClientProtocol
1495
+
1496
+ def __init__(self, debug = False):
1497
+ self.debug = debug
1498
+ self.path = "/"
1499
+ random.seed()
1500
+
1501
+ def clientConnectionFailed(self, connector, reason):
1502
+ reactor.stop()
1503
+
1504
+ def clientConnectionLost(self, connector, reason):
1505
+ reactor.stop()