defuze.me  Client
websocket.cpp
00001 /**************************************************************************
00002 ** defuze.me Epitech Innovative Project
00003 **
00004 ** Copyright 2010-2011
00005 **   Athena Calmettes - Jocelyn De La Rosa - Francois Gaillard
00006 **   Adrien Jarthon - Alexandre Moore - Luc Peres - Arnaud Sellier
00007 **
00008 ** All rights reserved.
00009 **************************************************************************/
00010 
00011 #include "websocket.hpp"
00012 #include "logger.hpp"
00013 #include <QHttpResponseHeader>
00014 #include <QCryptographicHash>
00015 #include <QApplication>
00016 
00017 using namespace Network;
00018 
00019 WebSocket::WebSocket(const QString& uri) : uri(uri), frame(0)
00020 {
00021     handshaking = false;
00022     QObject::connect(this, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(errorSlot(QAbstractSocket::SocketError)));
00023     QObject::connect(this, SIGNAL(connected()), SLOT(handshake()));
00024     QObject::connect(this, SIGNAL(readyRead()), SLOT(receive()));
00025 }
00026 
00027 WebSocket::~WebSocket()
00028 {
00029 }
00030 
00031 void        WebSocket::generateToken()
00032 {
00033     token.resize(16);
00034     for(int i = 0; i < 16; i++)
00035         token[i] = qrand() % 0xFF;
00036 }
00037 
00038 bool        WebSocket::validToken(const QByteArray& accept)
00039 {
00040     QByteArray hash = QCryptographicHash::hash(token.toBase64() + WebSocketSecret, QCryptographicHash::Sha1);
00041     return (accept == hash.toBase64());
00042 }
00043 
00044 void        WebSocket::connect()
00045 {
00046     generateToken();
00047     if (isOpen())
00048     {
00049         disconnectFromHost();
00050         readAll();
00051     }
00052     buffer.clear();
00053     connectToHost(uri.host(), uri.port());
00054     Logger::log(QString("WebSocket: connecting to %1:%2").arg(uri.host()).arg(uri.port()));
00055 }
00056 
00057 void        WebSocket::send(WebFrame& frame)
00058 {
00059     while (frame.remainingFrames() > 0)
00060     {
00061         QByteArray  data = frame.encoded();
00062         write(data);
00063         qApp->processEvents();
00064     }
00065 }
00066 
00067 void        WebSocket::onMessage(const QObject* receiver, const char* method)
00068 {
00069     QObject::connect(this, SIGNAL(messageSignal(QByteArray)), receiver, method);
00070 }
00071 
00072 void        WebSocket::onOpen(const QObject* receiver, const char* method)
00073 {
00074     QObject::connect(this, SIGNAL(openSignal()), receiver, method);
00075 }
00076 
00077 void        WebSocket::onClose(const QObject* receiver, const char* method)
00078 {
00079     QObject::connect(this, SIGNAL(closeSignal(QString)), receiver, method);
00080 }
00081 
00082 void        WebSocket::errorSlot(QAbstractSocket::SocketError error)
00083 {
00084     Q_UNUSED(error);
00085     Logger::log(QString("WebSocket: %1").arg(errorString()));
00086     emit closeSignal(errorString());
00087 }
00088 
00089 void        WebSocket::handshake()
00090 {
00091     handshaking = true;
00092     QString path = uri.path();
00093     if (uri.encodedQuery().size() > 0)
00094         path += "?" + QString::fromUtf8(uri.encodedQuery());
00095     write(QString("GET %1 HTTP/1.1\r\n").arg(path).toUtf8());
00096     write(QString("Host: %1\r\n").arg(uri.host()).toUtf8());
00097     write("Upgrade: websocket\r\n");
00098     write("Connection: Upgrade\r\n");
00099     write(QString("Sec-WebSocket-Key: %1\r\n").arg(QString::fromUtf8(token.toBase64())).toUtf8());
00100     write("Sec-WebSocket-Protocol: chat\r\n");
00101     write("Sec-WebSocket-Version: 8\r\n");
00102     write("\r\n");
00103 }
00104 
00105 bool    WebSocket::parseHandshake()
00106 {
00107     if (!canReadLine())
00108         return false;
00109 
00110     QByteArray  data = readLine();
00111     if (data == "\r\n") // Header end
00112     {
00113         QHttpResponseHeader header(QString::fromUtf8(buffer));
00114         if (header.statusCode() == 101 &&
00115             header.value("Upgrade") == "websocket" &&
00116             validToken(header.value("Sec-WebSocket-Accept").toUtf8()))
00117         {
00118             handshaking = false;
00119             Logger::log("WebSocket: connection ok");
00120             buffer.clear();
00121             emit openSignal();
00122         }
00123         else
00124         {
00125             Logger::log(QString("WebSocket: invalid handshake"));
00126             emit closeSignal(tr("Invalid handshake"));
00127             disconnectFromHost();
00128             return false;
00129         }
00130     }
00131     else
00132         buffer += data;
00133     return true;
00134 }
00135 
00136 bool    WebSocket::parseFrame()
00137 {
00138     if (frame)
00139         frame->append(this);
00140     else
00141     {
00142         frame = WebFrame::fromStream(this);
00143         if (!frame)
00144         {
00145             Logger::log("WebSocket: invalid framing data");
00146             emit closeSignal(tr("Invalid framing data"));
00147             disconnectFromHost();
00148             return false;
00149         }
00150     }
00151     if (frame && frame->complete())
00152     {
00153         emit messageSignal(frame->content());
00154         delete frame;
00155         frame = 0;
00156     }
00157     return true;
00158 }
00159 
00160 void        WebSocket::receive()
00161 {
00162     while (bytesAvailable())
00163     {
00164         if (handshaking)
00165         {
00166             if (!parseHandshake())
00167                 return;
00168         }
00169         else
00170         {
00171             if (!parseFrame())
00172                 return;
00173         }
00174     }
00175 }