TI's CC3100 websocket camera demo with Arducam mini ov5642 and freertos. Should work with other M3's. Work in progress test demo.

Dependencies:   mbed

http/server/WebSockHandler.cpp

Committer:
dflet
Date:
2015-09-11
Revision:
1:e448e81c416f
Parent:
0:400d8e75a8d0

File content as of revision 1:e448e81c416f:

//*****************************************************************************
// Copyright (C) 2014 Texas Instruments Incorporated
//
// All rights reserved. Property of Texas Instruments Incorporated.
// Restricted rights to use, duplicate or disclose this code are
// granted through contract.
// The program may not be used without the written permission of
// Texas Instruments Incorporated or against the terms and conditions
// stipulated in the agreement under which this program has been supplied,
// and under no circumstances can it be used with non-TI connectivity device.
//
//*****************************************************************************


/**
 * @addtogroup WebSockHandler
 *
 * @{
 */

//#include "HttpHeaders.h"
#include "HttpCore.h"
#include "HttpResponse.h"
#include "HttpRequest.h"
#include "HttpAuth.h"
#include "HttpDebug.h"
#include <string.h>
#include <stdlib.h>
#include "HttpConfig.h"
#include "HttpString.h"
#include "osi.h"
#include "WebSockHandler.h"
#include "httpserverapp.h"

// Include CC3200 SimpleLink headers
#include "cc3100_simplelink.h"


char *GlobRecvBuf;
int64_t GlobPayloadLen;
int64_t GlobRecvLen;
UINT8 RecvMore = 0;
char MaskKey[4];
UINT8 Mask;
UINT8 Ping = 0;
UINT8 Close = 0;

// WebSocket response status line strings
char    WS_STATUS_OK_STR[]                     =    "ok";
char    WS_STATUS_GOING_AWAY_STR[]   		   =    "server down";
char    WS_STATUS_ERROR_PROTOCOL_STR[]   	   =    "protocol error";
char    WS_STATUS_ERROR_DATATYPE_STR[]   	   =    "datatype not supported";
char    WS_STATUS_ERROR_ENCODING_STR[]         =    "data not interpreted";
char    WS_STATUS_ERROR_OVERFLOW_STR[]   	   =    "data too large";
char    WS_STATUS_ERROR_UNEXPECTED_STR[]       =    "unexpected event server";


/* This function parses the incoming data packet
* @return 1 if successful
		0 if failure
*/
/*

		0				1					2					3
	 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	+-+-+-+-+-------+-+-------------+-------------------------------+
	|F|R|R|R| opcode|M| Payload len |	 Extended payload length	|
	|I|S|S|S|  (4)	|A| 	(7) 	|			  (16/64)			|
	|N|V|V|V|		|S| 			|	(if payload len==126/127)	|
	| |1|2|3|		|K| 			|								|
	+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
	|	  Extended payload length continued, if payload len == 127	|
	+ - - - - - - - - - - - - - - - +-------------------------------+
	|								|Masking-key, if MASK set to 1	|
	+-------------------------------+-------------------------------+
	| Masking-key (continued)		|		   Payload Data 		|
	+-------------------------------- - - - - - - - - - - - - - - - +
	:					  Payload Data continued ...				:
	+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
	|					  Payload Data continued ...				|
	+---------------------------------------------------------------+


*/

int Payloadlength(struct HttpBlob * pData, UINT8 iter)
{
	int result = 0x0;

	while(iter != 0)
	{
		result = ((int) (*(pData->pData)) | result);
		if(iter != 1)
			result = result << 8;
		pData->pData += sizeof(UINT8);
		pData->uLength -= sizeof(UINT8);
		iter--;
	}

	return result;
}


int WSCore_DataRecv(UINT16 uConnection,struct HttpBlob * pData)
{
	UINT8 NextBlock;
	int lengthLeft;
	UINT16 final,RSV,Opcode;
	int PayloadLength;
	UINT8 iter;
	char *PayloadData;
	int RecvLength = (int)pData->uLength;


	if(!RecvMore)
	{
		//Parse the first byte
		NextBlock= *(pData->pData);
		pData->pData += sizeof(UINT8);
		pData->uLength -= sizeof(UINT8);

		//Mask bits to get header fields
		final = (NextBlock&(0x80));
		RSV = (NextBlock&(0x70));
		Opcode = ((NextBlock&(0x0F)));

		//If Final bit is set, this is the last data byte from client - terminating condition
		if(final)
			HttpDebug("Final datablock received \n\r");


		//Inform user about the opcode
			HttpDebug("Opcode is %d\n\r",Opcode);

			switch(Opcode)
			{
				case WS_TEXT:
					HttpDebug("Text data\n\r");
					break;
				case WS_BINARY:
					HttpDebug("Binary data\n\r");
					break;
				case WS_CLOSE:
					HttpDebug("Client has requested connection to be closed\n\r");
					Close = 1;
					WSStatusString(WS_STATUS_OK,pData);
					WSCore_DataSend(uConnection,*pData,Opcode);
					return 1;
				case WS_CONTINUATION:
					break;
				case WS_PING:
					HttpDebug("Ping received\n\r");
					Ping = 1;
					break;
				case WS_PONG:
					HttpDebug("Pong received\n\r");
					break;
				default:
					HttpDebug("This is unsupported\n\r");
					break;
			}


		///If header supports extension, send back error
		///Send error frame : TODO
		if(RSV)
			HttpDebug("Unsupported extension\n\r");

		//Parse the second byte
		NextBlock= *(pData->pData);
		pData->pData += sizeof(UINT8);
		pData->uLength -= sizeof(UINT8);

		Mask = (UINT8)(NextBlock&(0x80));
		PayloadLength = (NextBlock&(0x7F));

		//Payload length cases
		// If the payload length is 0x7E, the next 2 bytes represent the length
		if(PayloadLength == 0x7E)
		{
			iter = 2;
			PayloadLength = Payloadlength(pData, iter);
		}

		// If the payload length is 0x7F, the next 8 bytes represent the length
		if(PayloadLength == 0x7F)
		{
			iter = 8;
			PayloadLength = Payloadlength(pData, iter);
		}

		PayloadData = (char *)malloc(PayloadLength);
		if(PayloadData == NULL)
		{
			return 0;
		}
		memset(PayloadData,'\0',PayloadLength);
		GlobRecvBuf = PayloadData;
		GlobPayloadLen = PayloadLength;

		// If mask bit is set, the 4 bytes after payload length represent the masking key
		if(Mask)
		{
			memcpy(MaskKey,pData->pData,4);
			pData->pData += sizeof(UINT32);
			pData->uLength -= sizeof(UINT32);
		}

		int RecvLength = (int)pData->uLength;

		//Now, extract payload data
		memcpy(GlobRecvBuf,(const char *)(pData->pData),RecvLength);

		// Go back and get more data.
		if(PayloadLength > RecvLength)
		{
			GlobRecvLen = RecvLength; 	// Websocket header has 8 bytes that were also received.
			RecvMore = 1;
			return 1;
		}
	}

	else
	{
		memcpy(GlobRecvBuf+GlobRecvLen,(const char *)(pData->pData),RecvLength);
		GlobRecvLen += RecvLength;

		if(GlobRecvLen < GlobPayloadLen)
			return 1;
	}

	RecvMore = 0;


	if(Mask)
	{
		//UINT8 MaskBlock;
		char *pData = GlobRecvBuf;
		lengthLeft = GlobPayloadLen;
		UINT8 MaskIndex = 0;
		while(MaskIndex < 4)
		{
			NextBlock = (UINT8)(*pData);
			NextBlock ^= MaskKey[MaskIndex];
			MaskIndex++;
			*pData = NextBlock;
			pData += sizeof(UINT8);
			lengthLeft -= sizeof(UINT8);
			if(lengthLeft == 0)
				break;
			if(MaskIndex == 4)
				MaskIndex = 0;
		}

	}

	*(GlobRecvBuf+GlobPayloadLen) = '\0';

	struct HttpBlob PayLoad;
	PayLoad.pData = (UINT8 *)GlobRecvBuf;
	PayLoad.uLength = (UINT16)GlobPayloadLen;

	if(Ping)
	{
		HttpDebug("You were Pinged\n\r");
		//You must pong
		Opcode = 0x0A;
		WSCore_DataSend(uConnection,PayLoad,Opcode);
	}

	//free(pData->pData);

	//Send this data to main
	WebSocketRecvEventHandler(uConnection,GlobRecvBuf);


	return 1;

}

/*!
 * 	\brief 						 Sends data to a websocket client								
 *
 * 	\param[in] uConnection			Connection number on HTTP server. 
 * 	\param[in] PayLoad			Structure holding the payload data and the size of the data
 * 	\param[in] Opcode				User provides data type (text/binary/ping/pong/close).
 *
 * 	\return						1 - If packet was successfully received, parsed and sent to the user API
 *     							0 - Error
 */
int WSCore_DataSend(UINT16 uConnection, struct HttpBlob PayLoad, UINT8 Opcode)
{
	UINT16 usTotalLength;
	UINT8 usNextBlock;
	UINT16 usPayloadLen;	// The heap cannot support beyond 65kb
	char *pucPayLoadData = (char *)PayLoad.pData;

	usNextBlock = 0;
    
	if(Opcode != 0x02)
		usTotalLength = strlen(pucPayLoadData);
	else
		usTotalLength = PayLoad.uLength;

	do{

		///Is this the final packet?
		if(usTotalLength < FRAGMENT_LENGTH)
		{
			//final = 0x1;
			usNextBlock |= 0x80;
		}
		///Add opcode to the header
		usNextBlock |= Opcode;

		/// Add this byte to the sendpacket
		HttpResponse_AddCharToResponseHeaders(usNextBlock);

		///Reset byte
		usNextBlock = 0x0;
		///Mask bit is always set to 0 from server to client

		///PayloadLen field
		if(usTotalLength <= 125)
			usPayloadLen = usTotalLength;
		else
			usPayloadLen = (126);

		usNextBlock |= (UINT8)usPayloadLen;

		/// Add this byte to the sendpacket
		HttpResponse_AddCharToResponseHeaders(usNextBlock);

		/// If payload length is more than 125 bytes, we need 16 bits to represent it.
		if(usPayloadLen == (126))
		{

			if(usTotalLength < FRAGMENT_LENGTH)
				usPayloadLen = usTotalLength;
			else
				usPayloadLen = FRAGMENT_LENGTH;

			if(usPayloadLen >= FRAGMENT_LENGTH - 4)
				usPayloadLen = FRAGMENT_LENGTH - 4;

			usNextBlock = (char)(usPayloadLen>>8);
			HttpResponse_AddCharToResponseHeaders(usNextBlock);
			usNextBlock = (char)(usPayloadLen);
			HttpResponse_AddCharToResponseHeaders(usNextBlock);
		}

		HttpResponse_AddStringToResponseHeaders(pucPayLoadData,(usPayloadLen));

		if(!WS_SendPacket(uConnection))
			return 0;

		if(Opcode == WS_CLOSE)
		{
			sl_WebSocketCloseEvtHdlr();
			wait(1);
			(HttpCore_CloseConnection(uConnection));
			return 1;
		}

		usTotalLength -= (usPayloadLen);

		// Reset to continuation frame if packet is larger than max fragment size
		Opcode = WS_CONTINUATION;

		///Reset byte
		usNextBlock = 0x0;

		//Update pointer
		pucPayLoadData += usPayloadLen;

	}while(usTotalLength > 0);

	return 1;
}


/**
 * Returns status string according to status code - CHANGE
 */
void WSStatusString(UINT32 WS_Status, struct HttpBlob* status)
{
	HttpString_utoa(WS_Status, status);
	struct HttpBlob blob = {0, NULL};
    switch (WS_Status)
    {
    case WS_STATUS_OK:
        HttpBlobSetConst(blob, WS_STATUS_OK_STR);
        break;
    case WS_STATUS_GOING_AWAY:
        HttpBlobSetConst(blob, WS_STATUS_GOING_AWAY_STR);
        break;
    case WS_STATUS_ERROR_DATATYPE:
        HttpBlobSetConst(blob, WS_STATUS_ERROR_DATATYPE_STR);
        break;
    case WS_STATUS_ERROR_ENCODING:
        HttpBlobSetConst(blob, WS_STATUS_ERROR_ENCODING_STR);
        break;
    case WS_STATUS_ERROR_OVERFLOW:
        HttpBlobSetConst(blob, WS_STATUS_ERROR_OVERFLOW_STR);
        break;
    case WS_STATUS_ERROR_PROTOCOL:
        HttpBlobSetConst(blob, WS_STATUS_ERROR_PROTOCOL_STR);
        break;
    case WS_STATUS_ERROR_UNEXPECTED:
        HttpBlobSetConst(blob, WS_STATUS_ERROR_UNEXPECTED_STR);
        break;
    default:
        HttpDebug("Unknown response status \n\r");
        HttpAssert(0);
        break;
    }
    memcpy(status->pData + status->uLength, blob.pData, blob.uLength);
    return;
}

/// @}