Giter Site home page Giter Site logo

m8rge / cwebsocket Goto Github PK

View Code? Open in Web Editor NEW
252.0 29.0 60.0 338 KB

cWebsocket is lightweight websocket server library

License: MIT License

Arduino 14.53% Shell 0.73% HTML 9.00% C 75.74%
websocket websocket-server c microcontroller arduino rfc-6455 tiny library

cwebsocket's People

Contributors

johnou avatar m8rge avatar minghuascode avatar shankka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cwebsocket's Issues

Support for newer browser

Cwebsocket does not work with newer browsers, like latest Chrome or Firefox, probably because it only supports Websockets Draft 76, but not RFC 6455

Are you going to support RFC 6455?

base64 encode bug

base64 encoding routine is not working correctly, use this instead:

static size_t base64(char *dst, size_t dst_len, const unsigned char *src, size_t src_len)
{
static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int i, j, a, b, c;

if (base64len(src_len) > dst_len) return 0;
for (i = j = 0; i < src_len; i += 3)
{
a = src[i];
b = i + 1 >= src_len ? 0 : src[i + 1];
c = i + 2 >= src_len ? 0 : src[i + 2];

dst[j++] = b64[a >> 2];
dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
if (i + 1 < src_len) { dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; }
if (i + 2 < src_len) { dst[j++] = b64[c & 63]; }

}
while (j % 4 != 0) { dst[j++] = '='; }
dst[j++] = '\0';
return j;
}

Modified main.c to handle multiple clients using pthread_create

/*
 * Copyright (c) 2013 Putilov Andrey
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>

#include "websocket.h"

#define PORT 8088

//#define PACKET_DUMP

uint8_t buffer[BUF_LEN];
uint8_t gRecievedString[BUF_LEN];

char gHost[MAX_HANDSHAKE_FIELD_SIZE];
char gOrigin[MAX_HANDSHAKE_FIELD_SIZE];
char gResouce[MAX_HANDSHAKE_FIELD_SIZE];
char gKey[MAX_HANDSHAKE_FIELD_SIZE];

void nullHandshake(struct handshake *hs)
{
    hs->host = gHost;
    hs->origin = gOrigin;
    hs->resource = gResouce;
    hs->key = gKey;
    hs->frameType = WS_EMPTY_FRAME;
}

void error(const char *msg)
{
    perror(msg);
    exit(EXIT_FAILURE);
}

int safeSend(int clientSocket, const uint8_t *buffer, size_t bufferSize)
{
    #ifdef PACKET_DUMP
    printf("out packet:\n");
    fwrite(buffer, 1, bufferSize, stdout);
    printf("\n");
    #endif
    ssize_t written = send(clientSocket, buffer, bufferSize, 0);
    if (written == -1) {
        close(clientSocket);
        perror("send failed");
        return EXIT_FAILURE;
    }
    if (written != bufferSize) {
        close(clientSocket);
        perror("written not all bytes");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

void *clientWorker (void *arg) { 
    int *clientSocketPtr = (int *) arg;
    int clientSocket = *clientSocketPtr;

    memset(buffer, 0, BUF_LEN);
    size_t readedLength = 0;
    size_t frameSize = BUF_LEN;
    enum wsState state = WS_STATE_OPENING;
    uint8_t *data = NULL;
    size_t dataSize = 0;
    enum wsFrameType frameType = WS_INCOMPLETE_FRAME;
    struct handshake hs;

    nullHandshake(&hs);

    #define prepareBuffer frameSize = BUF_LEN; memset(buffer, 0, BUF_LEN);
    #define initNewFrame frameType = WS_INCOMPLETE_FRAME; readedLength = 0; memset(buffer, 0, BUF_LEN);

    while (frameType == WS_INCOMPLETE_FRAME) {
        ssize_t readed = recv(clientSocket, buffer+readedLength, BUF_LEN-readedLength, 0);
        if (readed == -1) {
            close(clientSocket);
            perror("recv failed");
            return NULL;
        }

    printf ("read %d bytes\n", (int) readed);

        #ifdef PACKET_DUMP
        printf("in packet:\n");
        fwrite(buffer, 1, readed, stdout);
        printf("\n");
        #endif

        readedLength += readed;
        assert(readedLength <= BUF_LEN);

        if (state == WS_STATE_OPENING) {
            frameType = wsParseHandshake(buffer, readedLength, &hs);
        } else {
            frameType = wsParseInputFrame(buffer, readedLength, &data, &dataSize);
        }

        if ((frameType == WS_INCOMPLETE_FRAME && readedLength == BUF_LEN) || frameType == WS_ERROR_FRAME) {
            if (frameType == WS_INCOMPLETE_FRAME)
                printf("buffer too small");
            else
                printf("error in incoming frame\n");

            if (state == WS_STATE_OPENING) {
                prepareBuffer;
                frameSize = sprintf((char *)buffer,
                                    "HTTP/1.1 400 Bad Request\r\n"
                                    "%s%s\r\n\r\n",
                                    versionField,
                                    version);
                safeSend(clientSocket, buffer, frameSize);
                break;
            } else {
                prepareBuffer;
                wsMakeFrame(NULL, 0, buffer, &frameSize, WS_CLOSING_FRAME);
                if (safeSend(clientSocket, buffer, frameSize) == EXIT_FAILURE)
                    break;
                state = WS_STATE_CLOSING;
                initNewFrame;
            }
        }

        if (state == WS_STATE_OPENING) {
            assert(frameType == WS_OPENING_FRAME);
            if (frameType == WS_OPENING_FRAME) {
                // if resource is right, generate answer handshake and send it
                if (strcmp(hs.resource, "/echo") != 0) {
                    frameSize = sprintf((char *)buffer, "HTTP/1.1 404 Not Found\r\n\r\n");
                    safeSend(clientSocket, buffer, frameSize);
                    break;
                }

                prepareBuffer;
                wsGetHandshakeAnswer(&hs, buffer, &frameSize);
                if (safeSend(clientSocket, buffer, frameSize) == EXIT_FAILURE) break;
                state = WS_STATE_NORMAL;
                initNewFrame;
            }
        } else {
            if (frameType == WS_CLOSING_FRAME) {
                if (state == WS_STATE_CLOSING) {
                    break;
                } else {
                    prepareBuffer;
                    wsMakeFrame(NULL, 0, buffer, &frameSize, WS_CLOSING_FRAME);
                    safeSend(clientSocket, buffer, frameSize);
                    break;
                }
            } else if (frameType == WS_TEXT_FRAME) {
                memcpy(gRecievedString, data, dataSize);
                gRecievedString[ dataSize ] = 0;

        printf ("IN: %s\n", gRecievedString);

                prepareBuffer;
                wsMakeFrame(gRecievedString, dataSize, buffer, &frameSize, WS_TEXT_FRAME);
                if (safeSend(clientSocket, buffer, frameSize) == EXIT_FAILURE) break;
                initNewFrame;
            }
        }
    } // read/write cycle

    close(clientSocket);
    printf ("Disconnecting\n");
    return NULL;
}

int main(int argc, char** argv)
{
    pthread_t threadID;

    int listenSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (listenSocket == -1) {
        error("create socket failed");
    }

    struct sockaddr_in local;
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = INADDR_ANY;
    local.sin_port = htons(PORT);
    if (bind(listenSocket, (struct sockaddr *) &local, sizeof(local)) == -1) {
        perror("bind failed");
    }

    if (listen(listenSocket, 1) == -1) {
        perror("listen failed");
    }
    printf("opened %s:%d\n", inet_ntoa(local.sin_addr), ntohs(local.sin_port));

    while (TRUE) {
        struct sockaddr_in remote;
        socklen_t sockaddrLen = sizeof(remote);
        int clientSocket = accept(listenSocket, (struct sockaddr*)&remote, &sockaddrLen);
        if (clientSocket == -1) {
            error("accept failed");
        }

        printf("connected %s:%d\n", inet_ntoa(remote.sin_addr), ntohs(remote.sin_port));
    int errorCode = pthread_create (&threadID, NULL, &clientWorker, &clientSocket);
    if (errorCode) {
        printf ("pthread_create FAILED on clientWorker error = %d", errorCode);
        break;
    } else pthread_detach(threadID);
    }

    close(listenSocket);
    return EXIT_SUCCESS;
}

Improper Handling of Invalid Resource IDs

In the main.c program, it appears that if I send an invalid resource indicator (e.g., "echox" instead of "echo"), the server will stop processing new connections. I believe this is because after "safeSend"ing the error response to the client the server needs to break unconditionally -- currently it only breaks if the safeSend fails.

Send Large String To The WebSocket Server

I am trying to create a websocket server in C++. I searched in google for a good easy code sample to do the work. I found your code is simple and working perfectly.

But when I try to send a large string to the Websocket server, server application crashed with the following error.
283: wsParseInputFrame: Assertion `payloadLength == inputLength-6-payloadFieldExtraBytes' failed.

Could you please help me to send a large string using web socket. where I need to change in the code to support large string size.

Many thanks to your CWebsocket code

Memory Leak

From my reading of the code (overall very well written) it looks like the following memory allocated for recievedString is never freed:

    } else if (frameType == WS_TEXT_FRAME) {
        uint8_t *recievedString = NULL;
        recievedString = malloc(dataSize+1);
        assert(recievedString);
        memcpy(recievedString, data, dataSize);
        recievedString[ dataSize ] = 0;

        prepareBuffer;
        wsMakeFrame(recievedString, dataSize, buffer, &frameSize, WS_TEXT_FRAME);
        if (safeSend(clientSocket, buffer, frameSize) == EXIT_FAILURE)
            break;
        initNewFrame;
    }

The code for wsMakeFrame() below also does NOT free the memory in recievedString:

void wsMakeFrame(const uint8_t *data, size_t dataLength,
uint8_t *outFrame, size_t *outLength, enum wsFrameType frameType)
{
assert(outFrame && *outLength);
assert(frameType < 0x10);
if (dataLength > 0)
assert(data);

outFrame[0] = 0x80 | frameType;

if (dataLength <= 125) {
outFrame[1] = dataLength;
_outLength = 2;
} else if (dataLength <= 0xFFFF) {
outFrame[1] = 126;
uint16_t payloadLength16b = htons(dataLength);
memcpy(&outFrame[2], &payloadLength16b, 2);
*outLength = 4;
} else {
outFrame[1] = 127;
memcpy(&outFrame[2], &dataLength, 8);
*outLength = 10;
}
memcpy(&outFrame[_outLength], data, dataLength);
*outLength+= dataLength;

}

My preferred solution would be to create create a maximum message size at compile time and not use malloc at all. malloc in embedded systems should be avoided at all costs IMO.

MemoryLeak: Not all calls to getUptoLinefeed are freeing memory with prepare macro

static char* getUptoLinefeed(const char *startFrom)
{
char *write_to = NULL;
uint8_t new_length = strstr_P(startFrom, rn) - startFrom;
assert(new_length);
write_to = (char *)malloc(new_length+1); //+1 for '\x00'
assert(write_to);
memcpy(write_to, startFrom, new_length);
write_to[ new_length ] = 0;

return write_to;

}

Suggest this code be rewritten to NOT use malloc(). getUptoLinefeed is being called in several places, and the memory is not freed in all cases. In the cases where the memory is freed it's being freed from a previous malloc using the prepare macro. If one were going to use malloc, one should be able to clearly see the corresponding free statement so that the code can be reviewed for memory leaks. Again suggest all uses of malloc be removed from this code (there are only a couple), there simply isn't a cogent reason for them to be in this code which is targeted for a an embedded system.

Multiple Clients

It appears the code (main.c) only allows one client at a time. This is primarily an application level issue I suppose to handle multiple connections, but.. if multiple connection support were added, would the library support that (e.g., threadsafe..)?

frameType set and then overwritten in wsParseHandshake

if (!hs->host || !hs->key || !connectionFlag || !upgradeFlag || subprotocolFlag
    || memcmp_P(versionString, version, strlen_P(version)) != 0)
{
    hs->frameType = WS_ERROR_FRAME;
}

hs->frameType = WS_OPENING_FRAME;
return hs->frameType;

Note that if frameType is ever set to WS_ERROR_FRAME, it will be overwritten. Suggest you add an else clause.

Firefox Handshake

Firefox handshake does not work;
[...]
Connection: keep-alive, Upgrade
[...]

is not supported with cwebsocket/websocket.c:126 (branch RFC6455)

if (memcmp_P(inputPtr, connectionField, strlen_P(connectionField)) == 0) {

Use something like strstr(..)

Memory Leaks?

Have you noticed any memory leaks with this? If I disconnect and reconnect several times the available heap size decreases significantly.

BUF_LEN is HUGE

Do clients really need to send 65535 bytes at once? You might also consider bumping the listen statement to support multiple clients. You could make the call to clientWorker(clientSocket) create a thread via conditional compilation.

define BUF_LEN 0xFFFF

Suggest you change BUF_LEN to something like 1024.
If this code is only going to support one client (and/or someone is going to send large packets) suggest you move this line out of clientWorker() and make it a global variable.

Currently:
clientWorker() {
uint8_t buffer[BUF_LEN];
}

Suggested:
uint8_t gBuffer[BUF_LEN];

clientWorker() {
ssize_t readed = recv(clientSocket, gBuffer+readedLength, BUF_LEN-readedLength, 0);
}

Where is SIZE_MAX declared in websocket.c?

It's used in the following code, and I don't know why one compiler caught this and another one didn't. Upon looking at the code I don't understand the intent. Shouldn't SIZE_MAX = 8, but if that is the case then the conditional is pointless, because memcpy is also set to copy 8 bytes. Even if the code was changed to "if (sizeof(payloadLength64b) > SIZE_MAX)" the code still would not make sense to me.

    } else if (payloadLength == 0x7F) {
        uint64_t payloadLength64b = 0;
        *payloadFieldExtraBytes = 8;
        memcpy(&payloadLength64b, &inputFrame[2], *payloadFieldExtraBytes);
        if (payloadLength64b > SIZE_MAX) {
            *frameType = WS_ERROR_FRAME;
            return 0;
        }
        payloadLength = (size_t)payloadLength64b;
    }

recv == 0

ssize_t readed = recv(clientSocket, gBuffer+readedLength, BUF_LEN-readedLength, 0);

I compile this lib use mipsel-openwrt-linux-gcc. run on router.

maybe void wsGetHandshakeAnswer(const struct handshake *hs, uint8_t *outFrame, *outLength)
have a bad responseKey
on x86 it is ok.

Incorrect handling of long size packet

Hi,

I've found you have some mistakes when handling long size packet.
In websocket.c , line 249, you have a mistake of endianness.
I patched it this way :
//memcpy(&payloadLength16b, &inputFrame[2], *payloadFieldExtraBytes);
payloadLength16b = (((uint16_t)inputFrame[2]) << 8 ) | inputFrame[3];

For sure you have the same mistake at line 255, but I couldn't test it.

Then you have another mistake in websocket.c , line 296 :
It's not :
if (payloadLength < inputLength-6-payloadFieldExtraBytes)
but :
if (payloadLength > inputLength-6-payloadFieldExtraBytes)

Or mabye it should be >= ...

Best regards,
Vincent.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.