Source code for socktools.http_mixin

"""
Python Sock tools: http_mixin.py - mixin to read HTTP requests from sockets
Copyright (C) 2016 GarethNelson

This file is part of python-sock-tools

python-sock-tools 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 2 of the License, or
(at your option) any later version.

python-sock-tools 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 python-sock-tools.  If not, see <http://www.gnu.org/licenses/>.

This module provides a mixin that gets HTTP requests from sockets, the basis of every webserver. No client implementation is provided - urllib or python-requests serves that purpose just fine.

Although it'd be cool, this does not currently support HTTP over UDP. Use it with a TCP socket. The HTTP support is also very basic at present.

Warning:
   Avoid using send_msg() on HTTP server sockets, this may cause various subtle and tricky to debug issues. Instead use the request object to reply.

"""

import eventlet
eventlet.monkey_patch() # this should be done in all modules that use eventlet as the first import, just in case
import StringIO

[docs]class HTTPRequest(object): """Represents an HTTP request Instances of this class are generated by HTTPMixin and used in the msg_data field. To respond to the request, use the reply() method. """ def __init__(self,verb='GET',path='/',headers={},client_sock=None,server_sock=None): self.verb = verb self.path = path self.client_sock = client_sock self.server_sock = server_sock
[docs] def reply(self,status=(200,'OK'),body='',headers={}): _headers = {'Content-Length':len(body)} headers.update(_headers) self.client_sock.sendall('HTTP/1.0 %d %s\r\n' % status) for k,v in headers.items(): self.client_sock.sendall('%s: %s\r\n' % (k,v)) self.client_sock.sendall('\r\n%s\n' % body) self.client_sock.close() try: del self.server_sock.known_peers[self.client_sock.getpeername()] except Exception,e: pass
[docs]class HTTPMixin(object): """A mixin for implementing HTTP Use this to get HTTP requests from the socket """
[docs] def parse_msg(self,args): client_addr,data = args buf = StringIO.StringIO(data) req_line = buf.readline().strip('\r\n') split_req = req_line.split() verb,path = split_req[0],split_req[1] headers = {} while self.active: in_line = buf.readline().strip('\r\n') if len(in_line)>1: header_k,header_v = in_line.split(': ') headers[header_k] = header_v else: break return verb,HTTPRequest(verb=verb,path=path,headers=headers,client_sock=self.known_peers[client_addr]['sock'],server_sock=self)
[docs] def do_real_read(self,s): """ Read a single HTTP request This method will read a single HTTP request without parsing it and then close the socket. Essentially we just read until the socket closes - which is a bit of a cheat but works. """ buf = StringIO.StringIO() remote_peer = s.getpeername() #hackish while self.active and self.known_peers.has_key(remote_peer): eventlet.greenthread.sleep(0) try: in_data = s.recv(1024) if not in_data: break else: buf.write(in_data) if in_data.endswith('\r\n\r\n'): break except: break retval = buf.getvalue() buf.close() if len(retval)==0: return '' return [remote_peer,retval]