Advertisement
yOPERO

ardws

Jan 26th, 2012
62
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.06 KB | None | 0 0
  1. /*
  2.     Websocketduino, a websocket implementation for webduino
  3.     Copyright 2010 Randall Brewer
  4.    
  5.     Some code and concept based off of Webduino library
  6.     Copyright 2009 Ben Combee, Ran Talbott
  7.  
  8.     Permission is hereby granted, free of charge, to any person obtaining a copy
  9.     of this software and associated documentation files (the "Software"), to deal
  10.     in the Software without restriction, including without limitation the rights
  11.     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12.     copies of the Software, and to permit persons to whom the Software is
  13.     furnished to do so, subject to the following conditions:
  14.  
  15.     The above copyright notice and this permission notice shall be included in
  16.     all copies or substantial portions of the Software.
  17.  
  18.     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19.     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20.     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21.     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22.     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23.     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24.     THE SOFTWARE.
  25.    
  26.     -------------
  27.    
  28.     Currently based off of "The Web Socket protocol" draft (v 75):
  29.     http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75
  30. */
  31.  
  32. #ifndef WEBSOCKET_H_
  33. #define WEBSOCKET_H_
  34. // CRLF characters to terminate lines/handshakes in headers.
  35. #define CRLF "\r\n"
  36. // Include '#define DEBUGGING true' before '#include <WebSocket.h>' in code to
  37. // enable serial debugging output.
  38. #ifndef DEBUGGING
  39.     #define DEBUGGING false
  40. #endif
  41. // Amount of time (in ms) a user may be connected before getting disconnected
  42. // for timing out (i.e. not sending any data to the server).
  43. #define TIMEOUT_IN_MS 30000
  44. #define BUFFER 32
  45. // ACTION_SPACE is how many actions are allowed in a program. Defaults to
  46. // 5 unless overwritten by user.
  47. #ifndef ACTION_SPACE
  48.     #define ACTION_SPACE 5
  49. #endif
  50. #define SIZE(array) (sizeof(array) / sizeof(*array))
  51.  
  52. #include <string.h>
  53. #include <stdlib.h>
  54. #include <WString.h>
  55.  
  56. class WebSocket {
  57.     public:
  58.         // Constructor for websocket class.
  59.         WebSocket(const char *urlPrefix = "/", int port = 8080);
  60.         // Processor prototype. Processors allow the websocket server to
  61.         // respond to input from client based on what the client supplies.
  62.         typedef void Action(WebSocket &socket, String &socketString);
  63.         // Start the socket listening for connections.
  64.         void begin();
  65.         // Handle connection requests to validate and process/refuse
  66.         // connections.
  67.         void connectionRequest();
  68.         // Loop to read information from the user. Returns false if user
  69.         // disconnects, server must disconnect, or an error occurs.
  70.         void socketStream(int socketBufferLink);
  71.         // Adds each action to the list of actions for the program to run.
  72.         void addAction(Action *socketAction);
  73.         // Custom write for actions.
  74.         void actionWrite(const char *str);
  75.     private:
  76.         Server socket_server;
  77.         Client socket_client;
  78.        
  79.         const char *socket_urlPrefix;
  80.         bool socket_reading;
  81.        
  82.         struct ActionPack {
  83.             Action *socketAction;
  84.             // String *socketString;
  85.         } socket_actions[ACTION_SPACE];
  86.         int socket_actions_population;
  87.        
  88.         // Discovers if the client's header is requesting an upgrade to a
  89.         // websocket connection.
  90.         bool analyzeRequest(int bufferLength);
  91.         // Disconnect user gracefully.
  92.         void disconnectStream();
  93.         // Send handshake header to the client to establish websocket
  94.         // connection.
  95.         void sendHandshake();
  96.         // Essentially a panic button to close all sockets currently open.
  97.         // Ideal for use with an actual button or as a safetey measure.
  98.         // void socketReset();
  99.         // Returns true if the action was executed. It is up to the user to
  100.         // write the logic of the action.
  101.         void executeActions(String socketString);
  102. };
  103.  
  104. WebSocket::WebSocket(const char *urlPrefix, int port) :
  105.     socket_server(port),
  106.     socket_client(255),
  107.     socket_actions_population(0),
  108.     socket_urlPrefix(urlPrefix)
  109. {}
  110.  
  111. void WebSocket::begin() {
  112.     socket_server.begin();
  113. }
  114.  
  115. void WebSocket::connectionRequest () {
  116.     // This pulls any connected client into an active stream.
  117.     socket_client = socket_server.available();
  118.     int bufferLength = BUFFER;
  119.     int socketBufferLength = BUFFER;
  120.    
  121.     // If there is a connected client.
  122.     if(socket_client.connected()) {
  123.         // Check and see what kind of request is being sent. If an upgrade
  124.         // field is found in this function, the function sendHanshake(); will
  125.         // be called.
  126.         #if DEBUGGING
  127.             Serial.println("*** Client connected. ***");
  128.         #endif
  129.         if(analyzeRequest(bufferLength)) {
  130.             // Websocket listening stuff.
  131.             // Might not work as intended since it may execute the stream
  132.             // continuously rather than calling the function once. We'll see.
  133.             #if DEBUGGING
  134.                 Serial.println("*** Analyzing request. ***");
  135.             #endif
  136.             // while(socket_reading) {
  137.                 #if DEBUGGING
  138.                     Serial.println("*** START STREAMING. ***");
  139.                 #endif
  140.                 socketStream(socketBufferLength);
  141.                 #if DEBUGGING
  142.                     Serial.println("*** DONE STREAMING. ***");
  143.                 #endif
  144.             // }
  145.         } else {
  146.             // Might just need to break until out of socket_client loop.
  147.             #if DEBUGGING
  148.                 Serial.println("*** Stopping client connection. ***");
  149.             #endif
  150.             disconnectStream();
  151.         }
  152.     }
  153. }
  154.  
  155. bool WebSocket::analyzeRequest(int bufferLength) {
  156.     // Use TextString ("String") library to do some sort of read() magic here.
  157.     String headerString = String(bufferLength);
  158.     char bite;
  159.    
  160.     #if DEBUGGING
  161.         Serial.println("*** Building header. ***");
  162.     #endif
  163.     while((bite = socket_client.read()) != -1) {
  164.         headerString.append(bite);
  165.     }
  166.    
  167.     #if DEBUGGING
  168.         Serial.println("*** DUMPING HEADER ***");
  169.         Serial.println(headerString.getChars());
  170.         Serial.println("*** END OF HEADER ***");
  171.     #endif
  172.    
  173.     if(headerString.contains("Upgrade: WebSocket")) {
  174.         #if DEBUGGING
  175.             Serial.println("*** Upgrade connection! ***");
  176.         #endif
  177.         sendHandshake();
  178.         #if DEBUGGING
  179.             Serial.println("*** SETTING SOCKET READ TO TRUE! ***");
  180.         #endif
  181.         socket_reading = true;
  182.         return true;
  183.     }
  184.     else {
  185.         #if DEBUGGING
  186.             Serial.println("Header did not match expected headers. Disconnecting client.");
  187.         #endif
  188.         return false;
  189.     }
  190. }
  191.  
  192. // This will probably have args eventually to facilitate different needs.
  193. void WebSocket::sendHandshake() {
  194.     #if DEBUGGING
  195.         Serial.println("*** Sending handshake. ***");
  196.     #endif
  197.     socket_client.write("HTTP/1.1 101 Web Socket Protocol Handshake");
  198.     socket_client.write(CRLF);
  199.     socket_client.write("Upgrade: WebSocket");
  200.     socket_client.write(CRLF);
  201.     socket_client.write("Connection: Upgrade");
  202.     socket_client.write(CRLF);
  203.     socket_client.write("WebSocket-Origin: file://");
  204.     socket_client.write(CRLF);
  205.     socket_client.write("WebSocket-Location: ws://192.168.1.170:8080/");
  206.     socket_client.write(CRLF);
  207.     socket_client.write(CRLF);
  208.     #if DEBUGGING
  209.         Serial.println("*** Handshake done. ***");
  210.     #endif
  211. }
  212.  
  213. void WebSocket::socketStream(int socketBufferLength) {
  214.     while(socket_reading) {
  215.         char bite;
  216.         // String to hold bytes sent by client to server.
  217.         String socketString = String(socketBufferLength);
  218.         // Timeout timeframe variable.
  219.         unsigned long timeoutTime = millis() + TIMEOUT_IN_MS;
  220.    
  221.         // While there is a client stream to read...
  222.         while((bite = socket_client.read()) && socket_reading) {
  223.             // Append everything that's not a 0xFF byte to socketString.
  224.             if((uint8_t)bite != 0xFF) {
  225.                 socketString.append(bite);
  226.             } else {
  227.                 // Timeout check.
  228.                 unsigned long currentTime = millis();
  229.                 if((currentTime > timeoutTime) && !socket_client.connected()) {
  230.                     #if DEBUGGING
  231.                         Serial.println("*** CONNECTION TIMEOUT! ***");
  232.                     #endif
  233.                     disconnectStream();
  234.                 }
  235.             }
  236.         }
  237.         // Assuming that the client sent 0xFF, we need to process the String.
  238.         // NOTE: Removed streamWrite() in favor of executeActions(socketString);
  239.         executeActions(socketString);
  240.     }
  241. }
  242.  
  243. void WebSocket::addAction(Action *socketAction) {
  244.     #if DEBUGGING
  245.         Serial.println("*** ADDING ACTIONS***");
  246.     #endif
  247.     if(socket_actions_population <= SIZE(socket_actions)) {
  248.         socket_actions[socket_actions_population++].socketAction = socketAction;
  249.     }
  250. }
  251.  
  252. void WebSocket::disconnectStream() {
  253.     #if DEBUGGING
  254.         Serial.println("*** TERMINATING SOCKET ***");
  255.     #endif
  256.     socket_reading = false;
  257.     socket_client.flush();
  258.     socket_client.stop();
  259.     socket_client = false;
  260.     #if DEBUGGING
  261.         Serial.println("*** SOCKET TERMINATED! ***");
  262.     #endif
  263. }
  264.  
  265. void WebSocket::executeActions(String socketString) {
  266.     int i;
  267.     #if DEBUGGING
  268.         Serial.print("*** EXECUTING ACTIONS ***");
  269.         Serial.print(socket_actions_population);
  270.         Serial.print("\n");
  271.     #endif
  272.     for(i = 0; i < socket_actions_population; ++i) {
  273.         #if DEBUGGING
  274.             Serial.print("* Action ");
  275.             Serial.print(i);
  276.             Serial.print("\n");
  277.         #endif
  278.         socket_actions[i].socketAction(*this, socketString);
  279.     }
  280. }
  281.  
  282. void WebSocket::actionWrite(const char *str) {
  283.     #if DEBUGGING
  284.         Serial.println(str);
  285.     #endif
  286.     socket_client.write((uint8_t)0x00);
  287.     socket_client.write(str);
  288.     socket_client.write((uint8_t)0xFF);
  289. }
  290. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement