Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import sys
- import socket, select
- import fcntl
- import email.parser
- import StringIO
- import datetime
- """
- See:
- http://docs.python.org/library/socket.html
- """
- __author__ = ['Caleb Burns', 'Ben DeMott']
- def main(argv=None):
- EOL1 = '\n\n'
- EOL2 = '\n\r\n'
- response = 'HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n'
- response += 'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n'
- response += 'Hello, world!'
- serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- # Tell the server socket file descriptor to destroy itself when this program ends.
- socketFlags = fcntl.fcntl(serversocket.fileno(), fcntl.F_GETFD)
- socketFlags |= fcntl.FD_CLOEXEC
- fcntl.fcntl(serversocket.fileno(), fcntl.F_SETFD, socketFlags)
- serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- serversocket.bind(('0.0.0.0', 8888))
- serversocket.listen(1)
- # Use asynchronous sockets.
- serversocket.setblocking(0)
- # Allow a queue of up to 128 requests (connections).
- serversocket.listen(128)
- # Listen to socket events on the server socket defined by the above bind() call.
- epoll = select.epoll()
- epoll.register(serversocket.fileno(), select.EPOLLIN)
- print "Epoll Server Started..."
- try:
- #The connection dictionary maps file descriptors (integers) to their corresponding network connection objects.
- connections = {}
- requests = {}
- responses = {}
- while True:
- # Ask epoll if any sockets have events and wait up to 1 second if no events are present.
- events = epoll.poll(1)
- # fileno is a file desctiptor.
- # event is the event code (type).
- for fileno, event in events:
- # Check for a read event on the socket because a new connection may be present.
- if fileno == serversocket.fileno():
- # connection is a new socket object.
- # address is client IP address. The format of address depends on the address family of the socket (i.e., AF_INET).
- connection, address = serversocket.accept()
- # Set new socket-connection to non-blocking mode.
- connection.setblocking(0)
- # Listen for read events on the new socket-connection.
- epoll.register(connection.fileno(), select.EPOLLIN)
- connections[connection.fileno()] = connection
- requests[connection.fileno()] = b''
- responses[connection.fileno()] = response
- # If a read event occured, then read the new data sent from the client.
- elif event & select.EPOLLIN:
- requests[fileno] += connections[fileno].recv(1024)
- # Once we're done reading, stop listening for read events and start listening for EPOLLOUT events (this will tell us when we can start sending data back to the client).
- if EOL1 in requests[fileno] or EOL2 in requests[fileno]:
- epoll.modify(fileno, select.EPOLLOUT)
- # Print request data to the console.
- epoll.modify(fileno, select.EPOLLOUT)
- data = requests[fileno]
- eol = data.find("\r\n") #this is the end of the FIRST line
- start_line = data[:eol] #get the contents of the first line (which is the protocol information)
- # method is POST|GET, etc
- method, uri, http_version = start_line.split(" ")
- # re-used facebooks httputil library (works well to normalize and parse headers)
- headers = HTTPHeaders.parse(data[eol:])
- print "\nCLIENT: FD:%s %s: '%s' %s" % (fileno, method, uri, datetime.datetime.now())
- # If the client is ready to receive data, sent it out response.
- elif event & select.EPOLLOUT:
- # Send response a single bit at a time until the complete response is sent.
- # NOTE: This is where we are going to use sendfile().
- byteswritten = connections[fileno].send(responses[fileno])
- responses[fileno] = responses[fileno][byteswritten:]
- if len(responses[fileno]) == 0:
- # Tell the socket we are no longer interested in read/write events.
- epoll.modify(fileno, 0)
- # Tell the client we are done sending data and it can close the connection. (good form)
- connections[fileno].shutdown(socket.SHUT_RDWR)
- # EPOLLHUP (hang-up) events mean the client has disconnected so clean-up/close the socket.
- elif event & select.EPOLLHUP:
- epoll.unregister(fileno)
- connections[fileno].close()
- del connections[fileno]
- finally:
- # Close remaining open socket upon program completion.
- epoll.unregister(serversocket.fileno())
- epoll.close()
- serversocket.close()
- #!/usr/bin/env python
- #
- # Copyright 2009 Facebook
- #
- # Licensed under the Apache License, Version 2.0 (the "License"); you may
- # not use this file except in compliance with the License. You may obtain
- # a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- # License for the specific language governing permissions and limitations
- # under the License.
- """HTTP utility code shared by clients and servers."""
- class HTTPHeaders(dict):
- """A dictionary that maintains Http-Header-Case for all keys.
- Supports multiple values per key via a pair of new methods,
- add() and get_list(). The regular dictionary interface returns a single
- value per key, with multiple values joined by a comma.
- >>> h = HTTPHeaders({"content-type": "text/html"})
- >>> h.keys()
- ['Content-Type']
- >>> h["Content-Type"]
- 'text/html'
- >>> h.add("Set-Cookie", "A=B")
- >>> h.add("Set-Cookie", "C=D")
- >>> h["set-cookie"]
- 'A=B,C=D'
- >>> h.get_list("set-cookie")
- ['A=B', 'C=D']
- >>> for (k,v) in sorted(h.get_all()):
- ... print '%s: %s' % (k,v)
- ...
- Content-Type: text/html
- Set-Cookie: A=B
- Set-Cookie: C=D
- """
- def __init__(self, *args, **kwargs):
- # Don't pass args or kwargs to dict.__init__, as it will bypass
- # our __setitem__
- dict.__init__(self)
- self._as_list = {}
- self.update(*args, **kwargs)
- # new public methods
- def add(self, name, value):
- """Adds a new value for the given key."""
- norm_name = HTTPHeaders._normalize_name(name)
- if norm_name in self:
- # bypass our override of __setitem__ since it modifies _as_list
- dict.__setitem__(self, norm_name, self[norm_name] + ',' + value)
- self._as_list[norm_name].append(value)
- else:
- self[norm_name] = value
- def get_list(self, name):
- """Returns all values for the given header as a list."""
- norm_name = HTTPHeaders._normalize_name(name)
- return self._as_list.get(norm_name, [])
- def get_all(self):
- """Returns an iterable of all (name, value) pairs.
- If a header has multiple values, multiple pairs will be
- returned with the same name.
- """
- for name, list in self._as_list.iteritems():
- for value in list:
- yield (name, value)
- def items(self):
- return [{key: value[0]} for key, value in self._as_list.iteritems()]
- def get_content_type(self):
- return dict.get(self, HTTPHeaders._normalize_name('content-type'), None)
- def parse_line(self, line):
- """Updates the dictionary with a single header line.
- >>> h = HTTPHeaders()
- >>> h.parse_line("Content-Type: text/html")
- >>> h.get('content-type')
- 'text/html'
- """
- name, value = line.split(":", 1)
- self.add(name, value.strip())
- @classmethod
- def parse(cls, headers):
- """Returns a dictionary from HTTP header text.
- >>> h = HTTPHeaders.parse("Content-Type: text/html\\r\\nContent-Length: 42\\r\\n")
- >>> sorted(h.iteritems())
- [('Content-Length', '42'), ('Content-Type', 'text/html')]
- """
- h = cls()
- for line in headers.splitlines():
- if line:
- h.parse_line(line)
- return h
- # dict implementation overrides
- def __setitem__(self, name, value):
- norm_name = HTTPHeaders._normalize_name(name)
- dict.__setitem__(self, norm_name, value)
- self._as_list[norm_name] = [value]
- def __getitem__(self, name):
- return dict.__getitem__(self, HTTPHeaders._normalize_name(name))
- def __delitem__(self, name):
- norm_name = HTTPHeaders._normalize_name(name)
- dict.__delitem__(self, norm_name)
- del self._as_list[norm_name]
- def get(self, name, default=None):
- return dict.get(self, HTTPHeaders._normalize_name(name), default)
- def update(self, *args, **kwargs):
- # dict.update bypasses our __setitem__
- for k, v in dict(*args, **kwargs).iteritems():
- self[k] = v
- @staticmethod
- def _normalize_name(name):
- """Converts a name to Http-Header-Case.
- >>> HTTPHeaders._normalize_name("coNtent-TYPE")
- 'Content-Type'
- """
- return "-".join([w.capitalize() for w in name.split("-")])
- if(__name__ == '__main__'):
- sys.exit(main(sys.argv))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement