-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathhttpflavor.py
240 lines (191 loc) · 7.67 KB
/
httpflavor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# -*- coding: utf-8 -*-
###############################################################################
# This file is part of twistedUlm.
#
# 2010, Christian Groeger <[email protected]>
#
# twistedUlm is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# twistedUlm is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with twistedUlm. If not, see <http://www.gnu.org/licenses/>.
###############################################################################
from basicflavor import BasicFlavor
from lookandfeel import history, btxInput
from cept import CHARS
from cepthtml import ceptHTML
class HTTPFlavor( BasicFlavor ):
"""
This specific server-flavor requests "BtxML"-encoded Webpages according to
the user's input from a webserver. It does some error handling for
connection- and HTTP-errors.
"""
connectionError="<cept><body><cs><aph><apr><apr>Ulm<sp>ist<sp>nicht<sp>erreichbar.</body></cept>"
otherError1="<cept><body><cs><aph><apr><apr>Fehler<sp>"
otherError2="<sp>ist<sp>aufgetreten.</body></cept>"
def __init__(self, verbosity, httpServer, httpSuffix, httpDelimiter):
self.verbosity=verbosity
self.httpServer=httpServer
self.httpSuffix=httpSuffix
self.httpDelimiter=httpDelimiter
self.history=history(size=100)
self.inputParser=btxInput(maxSize=23)
self.currentPage=ceptHTML() # this holds the currently parsed page
self.ignoreInput=False # locks the interpretation of input
self.retryOnError=True # retry getting error-pages only once
def getHTTP(self, wait, pagename, arguments):
"""
This has to be implemented in a class inherited from this one. So this
class can stay free of twisted-specific stuff.
"""
raise NotImplementedError
def processHTTP(self, pagename, arguments, status, html):
"""
This is called when getHTTP was finished loading the website. Status is
the standard HTTP response code or 0 for a connection error. This also
is the place where the error handling happens, this is done in two
stages: either the error-page returned by the server is a valid "BtxML"
document, then we display it. Or it's not, then we try to get the
error-specific page (eg. 404.btx), if this isn't successfull either we
display a standard error-message.
"""
#print "processing",pagename,"status",status
if not self.retryOnError:
# this is a error-page, set the correct name, so it's not 404
# so we can make sure the original pagename is inserted into the
# history and the correct page is reloaded
pagename=self.currentPage.name
if status==0:
# connection error
self.currentPage.parseHTML(self.connectionError)
self.retryOnError=True
self.sendPage()
elif status==200:
self.currentPage.parseHTML(html, pagename)
self.retryOnError=True
self.sendPage()
else:
# test if webserver returned a "BtxML" document
self.currentPage.parseHTML(html, pagename)
if self.currentPage.parseError:
if self.retryOnError:
# it didn't, so we try to get the error-specific page
self.getHTTP(0, str(status), [])
self.retryOnError=False
else:
# wasn't successfull either, abort and show a simple page
self.currentPage.parseHTML(self.otherError1+str(status)+self.otherError2)
self.retryOnError=True
self.sendPage()
else:
self.retryOnError=True
self.sendPage()
def sendPage(self):
"""
This is called by processHTTP. It sends the currently loaded page to
the terminal.
"""
self.inputParser.reset()
# the protocol handler calls dataSent when the data is sent, we don't want to process
# user input while we are sending
self.ignoreInput=True
self.sendCb(chr(CHARS['cof'])) # curser off
self.sendCb(self.currentPage.body)
self.sendCb(chr(CHARS['dct']))
if self.currentPage.loadpageTimeout>=0 and self.currentPage.loadpage!='':
# the current page requests a page forward, we put it into the inputparser to be handled when the current page is sent
self.inputParser.addPriorityAction( (btxInput.DELAYEDPAGE, (self.currentPage.loadpageTimeout, self.currentPage.loadpage)) )
elif self.currentPage.disconnect>=0:
# current page is a disconnect-page, disconnect in X seconds
self.closeCb(self.currentPage.disconnect)
elif self.currentPage.relayHost!='' and self.currentPage.relayPort>0:
# relay the connection to a remote host, we just do this immediately
self.relayCb(
self.currentPage.relayHost,
self.currentPage.relayPort,
self.currentPage.relayAfter,
self.currentPage.relayHeader)
self.inputParser.reset()
self.ignoreInput=False
else:
# cursor on
self.sendCb(chr(CHARS['con']))
def dataSent(self):
# remove the lock from the inputparser, input still was passed to it,
# but we didn't interpret it
self.ignoreInput=False
# poke the input handler to interpret input, that was arrived while
# we were sending
self.write('')
def hello(self):
self.getHTTP(0, "index", [])
def write(self, data):
answer=self.inputParser.putChars(data)
if not self.ignoreInput:
if len(answer)>0:
self.sendCb(answer)
(instruction, content) = self.inputParser.getInstruction()
if instruction == btxInput.RELOAD:
self.inputParser.reset()
self.getHTTP(0, self.currentPage.name, [])
elif instruction == btxInput.PREVIOUS:
previoussite=self.history.get()
if previoussite is not None:
# do nothing when there's no previous site
self.inputParser.reset()
self.getHTTP(0, previoussite, [])
elif instruction == btxInput.NEXT:
if self.currentPage.nextPage != '':
self.inputParser.reset()
if not self.currentPage.nohistory:
self.history.add(self.currentPage.name)
self.getHTTP(0, self.currentPage.nextPage, [])
elif instruction == btxInput.PAGE:
self.inputParser.reset()
if not self.currentPage.nohistory:
self.history.add(self.currentPage.name)
self.getHTTP(0, content, [])
elif instruction == btxInput.LINK:
linkto=self.currentPage.getLink(content)
if linkto is not None:
if not self.currentPage.nohistory:
self.history.add(self.currentPage.name)
self.inputParser.reset()
self.getHTTP(0, linkto, [])
elif instruction == btxInput.DELAYEDPAGE:
# this was the special instruction we added in sendPage()
self.inputParser.reset()
if not self.currentPage.nohistory:
self.history.add(self.currentPage.name)
self.getHTTP(content[0], content[1], [])
from twisted.web.client import getPage
from twisted.internet.reactor import callLater
class twistedHTTPFlavor( HTTPFlavor ):
"""
This just implements the getHTTP-part using twisted.
"""
def getHTTP(self, wait, pagename, arguments):
if wait>0:
self.gethttpschedule = callLater(wait, self.getHTTP, 0, pagename, arguments)
else:
url=self.httpServer+pagename+self.httpSuffix
def succes(value):
self.processHTTP( pagename, arguments, 200, value)
def error(error):
self.processHTTP( pagename, arguments, int(error.value.status), error.value.response)
#print error.value.__dict__
getPage(url).addCallbacks(callback=succes, errback=error )
def connectionLost(self, reason):
try:
if self.gethttpschedule.active():
self.gethttpschedule.cancel()
except(AttributeError):
pass
HTTPFlavor.connectionLost(self, reason)