This example shows how to create a simple HTTP server.
We import the classes and modules that will be needed by the application. The most relevant are the classes from the java.net module.
from java.io import BufferedInputStream, BufferedOutputStream, IOException, \
OutputStream
from java.lang import Exception, Runnable, String, Thread
from java.net import BindException, ServerSocket, SocketException
from java.util import LinkedList
from android.os import Handler, Looper
from android.view import View
from android.widget import Button, LinearLayout, ScrollView, TextView, Toast
from serpentine.activities import Activity
We define a class based on the specialised Activity class provided by the
serpentine.activities
module. This represents the application, and will be
used to present a graphical interface to the user.
class HTTPServerActivity(Activity):
__interfaces__ = [Runnable, View.OnClickListener]
__fields__ = {"inputStream": BufferedInputStream}
START_TEXT = "Start server"
STOP_TEXT = "Stop server"
SERVING = "Serving on port "
NOT_SERVING = "Not serving"
PORT_IN_USE = "Port already in use"
OK = "HTTP/1.1 200 OK"
BAD_REQUEST = "HTTP/1.1 400 Bad Request"
PAGE_TEXT = (
"<html><body><h1>HTTPServerActivity</h1>\n"
"<p>This is a page served by the HTTP Server example.</p>\n"
"</body></html>"
)
ERROR_TEXT = (
"<html><body><h1>Bad Request</h1>\n"
"<p>The server failed to understand the request. Sorry!</p>\n"
"</body></html>"
)
def __init__(self):
Activity.__init__(self)
def onCreate(self, bundle):
Activity.onCreate(self, bundle)
# Define whether the server is running.
self.running = False
We create a label to show the server's status, a button to allow the user to start and stop it, and a label in a scroll view to show a log of requests that the server has received.
self.label = TextView(self)
self.button = Button(self)
self.button.setText(self.START_TEXT)
self.button.setOnClickListener(self)
self.requestLabel = TextView(self)
self.scrollView = ScrollView(self)
self.scrollView.addView(self.requestLabel)
We also create a handler that will receive messages sent from the worker thread.
self.handler = MessageHandler(Looper.getMainLooper(), self)
Finally, we put all the widgets in a layout and make that the main content of the application.
layout = LinearLayout(self)
layout.setOrientation(layout.VERTICAL)
layout.addView(self.label)
layout.addView(self.button)
layout.addView(self.scrollView)
self.setContentView(layout)
def onClick(self, view):
if not self.running:
# Create a server socket to handle new connections.
try:
self.serverSocket = ServerSocket(8080)
except BindException:
Toast.makeText(self, self.PORT_IN_USE, Toast.LENGTH_SHORT).show()
return
# Indicate that the server is running, mostly for the GUI but also
# for the server thread.
self.running = True
# Reset the input stream so that we can detect when there is an
# active input stream.
self.inputStream = None
# Start a new thread with this instance as its runnable object.
self.thread = Thread(self)
self.thread.start()
# Change the button and status label text to indicate that the
# server is running.
self.button.setText(self.STOP_TEXT)
self.label.setText(self.SERVING + str(self.serverSocket.getLocalPort()))
else:
self.stopServer()
def stopServer(self):
# Indicate that the server is not running, mostly for the GUI but
# also for the server thread.
self.running = False
# Close the server socket so that new connections cannot be made.
self.serverSocket.close()
# Close the input stream, if opened, so that pending connection
# socket reads are interrupted.
if self.inputStream != None:
self.inputStream.close()
# Wait until the thread has finished.
self.thread.join()
# Reset the button and status label.
self.button.setText(self.START_TEXT)
self.label.setText(self.NOT_SERVING)
@args(void, [String])
def addLogText(self, text):
s = str(self.requestLabel.getText()) + text
self.requestLabel.setText(s)
self.scrollView.fullScroll(View.FOCUS_DOWN)
def run(self):
while self.running:
try:
self.writeLog("Waiting...\n")
socket = self.serverSocket.accept()
self.inputStream = BufferedInputStream(socket.getInputStream())
output = BufferedOutputStream(socket.getOutputStream())
self.writeLog("Started handling requests...\n")
while self.running:
# Handle requests on the same socket until the connection
# is closed by the client.
request = []
while self.running:
l = []
while self.running:
b = self.inputStream.read()
if b == 10: # newline
l.add(byte(b))
break
elif b == -1:
raise IOException()
l.add(byte(b))
s = String(array(l), "ASCII")
if s == "\r\n":
break
request.add(s)
self.handleRequest(request, output)
self.writeLog("Handled\n")
except (SocketException,), se:
self.writeLog(str(se) + "\n")
except (IOException,), ioe:
self.writeLog(str(ioe) + "\n")
self.writeLog("Stopped\n")
message = self.handler.obtainMessage(1, None)
message.sendToTarget()
@args(void, [String])
def writeLog(self, s):
message = self.handler.obtainMessage(0, s)
message.sendToTarget()
@args(void, [LinkedList(String), OutputStream])
def handleRequest(self, lines, output):
request = lines.pollFirst()
if request == None:
self.writeResponse(self.BAD_REQUEST, self.ERROR_TEXT, output)
self.writeLog(request)
headers = {}
for line in lines:
at = line.indexOf(":")
if at != -1:
headers[line[:at]] = line[at + 1:]
pieces = request.split(" ")
if len(pieces) >= 2:
if pieces[0] == "GET":
if pieces[1] == "/":
self.writeResponse(self.OK, self.PAGE_TEXT, output)
return
self.writeResponse(self.BAD_REQUEST, self.ERROR_TEXT, output)
@args(void, [String, String, OutputStream])
def writeResponse(self, status, text, output):
b = text.getBytes("UTF-8")
l = [status,
"Content-Type: text/html",
"Content-Length: " + str(len(b))]
s = ""
for i in l:
s += i + "\r\n"
s += "\r\n"
#self.writeLog(s + text)
output.write(s.getBytes("US-ASCII"))
output.flush()
output.write(b)
output.flush()
class MessageHandler(Handler):
@args(void, [Looper, HTTPServerActivity])
def __init__(self, looper, activity):
Handler.__init__(self, looper)
self.activity = activity
def handleMessage(self, inputMessage):
if inputMessage.what == 0:
self.activity.addLogText(str(inputMessage.obj))
elif inputMessage.what == 1:
self.activity.stopServer()