Problem With Python Sockets: How To Get Reliably POSTed data whatever the browser?

Question!

I wrote small Python+Ajax programs (listed at the end) with socket module to study the COMET concept of asynchronous communications.
The idea is to allow browsers to send messages real time each others via my python program.
The trick is to let the "GET messages/..." connexion opened waiting for a message to answer back.
My problem is mainly on the reliability of what I have via socket.recv...
When I POST from Firefox, it is working well.
When I POST from Chrome or IE, the "data" I get in Python is empty.

Does anybody know about this problem between browsers?
Are some browsers injecting some EOF or else characters killing the receiving of "recv"?
Is there any solution known to this problem?

Thanks for your help,

J.

The server.py in Python:

 import socket
connected={}
def inRequest(text):
   content=''
   if text[0:3]=='GET':
      method='GET'
   else:
      method='POST'
      k=len(text)-1
      while k>0 and text[k]!='\n' and text[k]!='\r':
         k=k-1
      content=text[k+1:]
   text=text[text.index(' ')+1:]
   url=text[:text.index(' ')]
   return {"method":method,"url":url,"content":content}

mySocket = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
mySocket.bind ( ( '', 80 ) )
mySocket.listen ( 10 )
while True:
   channel, details = mySocket.accept()
   data=channel.recv(4096)
   req=inRequest(data)
   url=req["url"]
   if url=="/client.html" or url=="/clientIE.html":
      f=open('C:\\async\\'+url)
      channel.send ('HTTP/1.1 200 OK\n\n'+f.read())
      f.close()
      channel.close()
   elif '/messages' in url:
      if req["method"]=='POST':
         target=url[10:]
         if target in connected:
            connected[target].send("HTTP/1.1 200 OK\n\n"+req["content"])
            print req["content"]+" sent to "+target
            connected[target].close()
            channel.close()
      elif req["method"]=='GET':
         user=url[10:]
         connected[user]=channel
         print user+' is connected'

The client.html in HTML+Javascript:

<html>
<head>
    <script>
    	var user=''
    	function post(el) {
    		if (window.XMLHttpRequest) {
    			var text=el.value;
    			var req=new XMLHttpRequest();
    			el.value='';
    			var target=document.getElementById('to').value
    		}
    		else if (window.ActiveXObject) {
    			var text=el.content;
    			var req=new ActiveXObject("Microsoft.XMLHTTP");
    			el.content='';
    		}
    		else 
    			return;
    		req.open('POST','messages/'+target,true)
    		req.send(text);
    	}
    	function get(u) {
    		if (user=='')
    			user=u.value
    		var req=new XMLHttpRequest()
    		req.open('GET','messages/'+user,true)
    		req.onload=function() {
    			var message=document.createElement('p');
    			message.innerHTML=req.responseText;
    			document.getElementById('messages').appendChild(message);
    			get(user);
    		}
    		req.send(null)
    	}
    </script>
</head>
<body>
<span>From</span>
<input id="user"/>
<input type="button" value="sign in" onclick="get(document.getElementById('user'))"/>
<span>To</span>
<input id="to"/>
<span>:</span>
<input id="message"/>

<input type="button" value="post" onclick="post(document.getElementById('message'))"/>
<div id="messages">
</div>
</body>
</html>


Answers

The problem you have is that

  • your tcp socket handling isn't reading as much as it should
  • your http handling is not complete

I recommend the following lectures:

See the example below for a working http server that can process posts

index = '''
<html>
    <head>
    </head>
    <body>
        <form action="/" method="POST">
            <textarea name="foo"></textarea>
            <button type="submit">post</button>
        </form>
        <h3>data posted</h3>
        <div>
            %s
        </div>
    </body>
</html>
'''

bufsize = 4048
import socket
import re
from urlparse import urlparse

class Headers(object):
    def __init__(self, headers):
        self.__dict__.update(headers)

    def __getitem__(self, name):
        return getattr(self, name)

    def get(self, name, default=None):
        return getattr(self, name, default)

class Request(object):
    header_re = re.compile(r'([a-zA-Z-]+):? ([^\r]+)', re.M)

    def __init__(self, sock):
        header_off = -1
        data = ''
        while header_off == -1:
            data += sock.recv(bufsize)
            header_off = data.find('\r\n\r\n')
        header_string = data[:header_off]
        self.content = data[header_off+4:]

        lines = self.header_re.findall(header_string)
        self.method, path = lines.pop(0)
        path, protocol = path.split(' ')
        self.headers = Headers(
            (name.lower().replace('-', '_'), value)
            for name, value in lines
        )

        if self.method in ['POST', 'PUT']:
            content_length = int(self.headers.get('content_length', 0))
            while len(self.content) <  content_length:
                self.content += sock.recv(bufsize)

        self.query = urlparse(path)[4]

acceptor = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
acceptor.setsockopt(
    socket.SOL_SOCKET,
    socket.SO_REUSEADDR,
    1,
)
acceptor.bind(('', 2501 ))
acceptor.listen(10)

if __name__ == '__main__':
    while True:
        sock, info = acceptor.accept()
        request = Request(sock)
        sock.send('HTTP/1.1 200 OK\n\n' + (index % request.content) )
        sock.close()


I would recommend using a JS/Ajax library on the client-side just to eliminate the possibility of cross-browser issues with your code. For the same reason I would recommend using a python http server library like SimpleHTTPServer or something from Twisted if the former does not allow low-level control.

Another idea - use something like Wireshark to check what's been sent by the browsers.



This video can help you solving your question :)
By: admin