GainSpan Wi-Fi library see: http://mbed.org/users/gsfan/notebook/gainspan_wifi/

Dependents:   GSwifi_httpd GSwifi_websocket GSwifi_tcpclient GSwifi_tcpserver ... more

Fork of GSwifi by gs fan

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers GSwifi_httpd.cpp Source File

GSwifi_httpd.cpp

Go to the documentation of this file.
00001 /* Copyright (C) 2013 gsfan, MIT License
00002  *
00003  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00004  * and associated documentation files (the "Software"), to deal in the Software without restriction,
00005  * including without limitation the rights to use, copy, modify, merge, publish, distribute,
00006  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
00007  * furnished to do so, subject to the following conditions:
00008  *
00009  * The above copyright notice and this permission notice shall be included in all copies or
00010  * substantial portions of the Software.
00011  *
00012  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00013  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00014  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00015  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00016  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00017  */
00018 /** @file
00019  * @brief Gainspan wi-fi module library for mbed
00020  * GS1011MIC, GS1011MIP, GainSpan WiFi Breakout, etc.
00021  */
00022 
00023 #include "GSwifi_conf.h"
00024 #ifdef GS_ENABLE_HTTPD
00025 
00026 #include "dbg.h"
00027 #include "mbed.h"
00028 #include "GSwifi.h"
00029 #include "sha1.h"
00030 #include <string.h>
00031 
00032 #define MIMETABLE_NUM 9
00033 static const struct {
00034     char ext[5];
00035     char type[24];
00036 } mimetable[MIMETABLE_NUM] = {
00037     {"txt", "text/plain"},  // default
00038     {"html", "text/html"},
00039     {"htm", "text/html"},
00040     {"css", "text/css"},
00041     {"js", "application/javascript"},
00042     {"jpg", "image/jpeg"},
00043     {"png", "image/png"},
00044     {"gif", "image/gif"},
00045     {"ico", "image/x-icon"},
00046 };
00047 
00048 int GSwifi::httpd (int port) {
00049     int i;
00050     char cmd[GS_CMD_SIZE];
00051 
00052     if (! _connect || _status != GSSTAT_READY) return -1;
00053 
00054     memset(&_httpd, 0, sizeof(_httpd));
00055     for (i = 0; i < 16; i ++) {
00056         _httpd[i].mode = GSHTTPDMODE_REQUEST;
00057     }
00058     _handler_count = 0;
00059 
00060     sprintf(cmd, "AT+NSTCP=%d", port);
00061     if (command(cmd, GSRES_CONNECT)) return -1;
00062 
00063     newSock(_cid, GSTYPE_SERVER, GSPROT_HTTPD, this, &GSwifi::poll_httpd);
00064     return _cid;
00065 }
00066 
00067 void GSwifi::poll_httpd (int cid, int len) {
00068     int i, j, flg = 0;
00069     char c;
00070 
00071     if (len == 0) {
00072         // start request
00073         _httpd[cid].mode = GSHTTPDMODE_REQUEST;
00074         _httpd[cid].len = 0;
00075         _httpd[cid].keepalive = 0;
00076 #ifdef GS_ENABLE_WEBSOCKET
00077         _httpd[cid].websocket = 0;
00078 #endif
00079         return;
00080     }
00081 
00082 #ifdef GS_ENABLE_WEBSOCKET
00083     if (_httpd[cid].mode >= GSHTTPDMODE_WEBSOCKET) {
00084         poll_websocket(cid, len);
00085         return;
00086     }
00087 #endif
00088 
00089   while (_gs_sock[cid].connect && (! _gs_sock[cid].data->isEmpty())) {
00090     flg = 0;
00091     if (_httpd[cid].buf == NULL) {
00092         _httpd[cid].buf = (char*)malloc(HTTPD_BUF_SIZE);
00093     }
00094     // get 1 line
00095     for (j = 0; j < len; j ++) {
00096         _gs_sock[cid].data->dequeue(&c);
00097         if (c == '\r') continue;
00098         if (c == '\n' && _httpd[cid].mode != GSHTTPDMODE_BODY) break;
00099         
00100         if (_httpd[cid].len < HTTPD_BUF_SIZE - 1) {
00101             _httpd[cid].buf[_httpd[cid].len] = c; 
00102         }
00103         _httpd[cid].len ++;
00104         if (_httpd[cid].mode == GSHTTPDMODE_BODY && _httpd[cid].len >= _httpd[cid].length) break; // end of body
00105     }
00106     if (j >= len) return; // continue
00107     if (_httpd[cid].len < HTTPD_BUF_SIZE) {
00108         _httpd[cid].buf[_httpd[cid].len] = 0;
00109         DBG("httpd %d: %d %s (%d)\r\n", cid, _httpd[cid].mode, _httpd[cid].buf, _httpd[cid].len);
00110     }
00111 
00112     // parse
00113     switch (_httpd[cid].mode) {
00114     case GSHTTPDMODE_REQUEST:
00115         if (strnicmp(_httpd[cid].buf, "GET ", 4) == 0) {
00116             _httpd[cid].type = GSPROT_HTTPGET;
00117             j = 4;
00118         } else
00119         if (strnicmp(_httpd[cid].buf, "POST ", 5) == 0) {
00120             _httpd[cid].type = GSPROT_HTTPPOST;
00121             j = 5;
00122         } else {
00123             _httpd[cid].mode = GSHTTPDMODE_ERROR;
00124             break;
00125         }
00126 
00127         // get uri
00128         for (i = j; i < _httpd[cid].len; i ++) {
00129             if (_httpd[cid].buf[i] == ' ') break;
00130         }
00131         i = i - j;
00132         if (i) {
00133             if (_httpd[cid].uri == NULL) {
00134                 _httpd[cid].uri = (char*)malloc(HTTPD_URI_SIZE);
00135             }
00136             strncpy(_httpd[cid].uri, &_httpd[cid].buf[j], i);
00137             _httpd[cid].uri[i] = 0;
00138         }
00139         _httpd[cid].mode = GSHTTPDMODE_HEAD;
00140         _httpd[cid].length = 0;
00141         DBG("uri: %s\r\n", _httpd[cid].uri);
00142         break;
00143         
00144     case GSHTTPDMODE_HEAD:
00145         if (_httpd[cid].len == 0) {
00146             // blank line (end of header)
00147             _httpd[cid].mode = GSHTTPDMODE_BODY;
00148             if (_httpd[cid].length == 0) flg = 1; // no body
00149 #ifdef GS_ENABLE_WEBSOCKET
00150             if (_httpd[cid].websocket && _httpd[cid].websocket_key) {
00151                 // enter websocket
00152                 _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET;
00153                 _httpd[cid].len = 0;
00154                 flg = 1;
00155             }
00156 #endif
00157         } else
00158         if (strnicmp(_httpd[cid].buf, "Content-Length: ", 16) == 0) {
00159             _httpd[cid].length = atoi(&_httpd[cid].buf[16]);
00160         } else
00161         if (strnicmp(_httpd[cid].buf, "Connection: Keep-Alive", 22) == 0) {
00162             if (! _httpd[cid].keepalive) {
00163                 _httpd[cid].keepalive = HTTPD_KEEPALIVE;
00164             }
00165 #ifdef GS_ENABLE_WEBSOCKET
00166         } else
00167         if (strnicmp(_httpd[cid].buf, "Upgrade: websocket", 18) == 0) {
00168             if (! _httpd[cid].websocket) _httpd[cid].websocket = 1;
00169         } else
00170         if (strnicmp(_httpd[cid].buf, "Sec-WebSocket-Version: ", 23) == 0) {
00171             _httpd[cid].websocket = atoi(&_httpd[cid].buf[23]);
00172         } else
00173         if (strnicmp(_httpd[cid].buf, "Sec-WebSocket-Key: ", 19) == 0) {
00174             if (_httpd[cid].websocket_key == NULL) {
00175                 _httpd[cid].websocket_key = (char*)malloc(30);
00176             }
00177             strncpy(_httpd[cid].websocket_key, &_httpd[cid].buf[19], 30);
00178 #endif
00179         }
00180         break;
00181 
00182     case GSHTTPDMODE_BODY:
00183         if (_httpd[cid].len >= _httpd[cid].length) {
00184             DBG("body: %s\r\n", _httpd[cid].buf);
00185             flg = 1;
00186         }
00187         break;
00188 
00189     }
00190 
00191 #ifdef GS_ENABLE_WEBSOCKET
00192     if (flg && _httpd[cid].mode == GSHTTPDMODE_WEBSOCKET) {
00193         // websocket
00194         i = get_handler(_httpd[cid].uri);
00195         if (i >= 0 && _handler[i].onHttpCgi) {
00196             _httpd[cid].host = _gs_sock[cid].host;
00197             _httpd[cid].file = NULL;
00198             _httpd[cid].query = NULL;
00199 
00200             send_websocket_accept(cid);
00201         } else {
00202             // not found
00203             send_httpd_error(cid, 403);
00204         }
00205         break; // exit while
00206 
00207     } else
00208 #endif
00209     if (flg) {
00210         // http request
00211         _httpd[cid].buf[_httpd[cid].len] = 0;
00212 
00213         i = get_handler(_httpd[cid].uri);
00214         if (i >= 0) {
00215             _httpd[cid].host = _gs_sock[cid].host;
00216             j = strlen(_handler[i].uri);
00217             _httpd[cid].file = &_httpd[cid].uri[j];
00218             _httpd[cid].query = NULL;
00219             for (; j < strlen(_httpd[cid].uri); j ++) {
00220                 if (_httpd[cid].uri[j] == '?') {
00221                     // query string
00222                     _httpd[cid].uri[j] = 0;
00223                     _httpd[cid].query = &_httpd[cid].uri[j + 1];
00224                     break;
00225                 }
00226             }
00227 
00228             if (_handler[i].dir) {
00229                 // file
00230                 httpd_request(cid, &_httpd[cid], _handler[i].dir);
00231                 flg = 1;
00232             } else
00233             if (_handler[i].onHttpCgi) {
00234                 // cgi
00235                 _handler[i].onHttpCgi(cid, &_httpd[cid]);
00236                 _httpd[cid].keepalive = 0;
00237                 LOG("%d.%d.%d.%d ", _httpd[cid].host.getIp()[0], _httpd[cid].host.getIp()[1], _httpd[cid].host.getIp()[2], _httpd[cid].host.getIp()[3]);
00238                 LOG("%s %s %d 200 -\r\n", _httpd[cid].type == GSPROT_HTTPGET ? "GET" : "POST", _httpd[cid].uri, _httpd[cid].length);
00239                 flg = 1;
00240             }
00241         } else {
00242             // not found
00243             send_httpd_error(cid, 403);
00244         }
00245         
00246         if (_httpd[cid].keepalive) {
00247             _httpd[cid].mode = GSHTTPDMODE_REQUEST;
00248             _httpd[cid].len = 0;
00249             _httpd[cid].length = 0;
00250             _httpd[cid].keepalive --;
00251         } else {
00252             close(cid);
00253         }
00254     }
00255 
00256     if (_httpd[cid].mode == GSHTTPDMODE_ERROR) {
00257         send_httpd_error(cid, 400);
00258     }
00259 
00260     _httpd[cid].len = 0;
00261   } // while
00262 }
00263 
00264 int GSwifi::get_handler (char *uri) {
00265     int i, j;
00266 
00267     for (i = 0; i < _handler_count; i ++) {
00268         j = strlen(_handler[i].uri);
00269         if (strncmp(uri, _handler[i].uri, j) == NULL) {
00270             // found
00271             return i;
00272         }
00273     }
00274     return -1;
00275 }
00276 
00277 int GSwifi::httpd_request (int cid, GS_httpd *gshttpd, char *dir) {
00278     FILE *fp;
00279     int i, len;
00280     char buf[HTTPD_BUF_SIZE];
00281     char file[HTTPD_URI_SIZE];
00282 
00283     strcpy(file, dir);
00284     strcat(file, gshttpd->file);
00285     if (file[strlen(file) - 1] == '/') {
00286         strcat(file, "index.html");
00287     }
00288     DBG("file: %s\r\n", file);
00289     
00290     fp = fopen(file, "r");
00291     if (fp) {
00292         send(cid, "HTTP/1.1 200 OK\r\n", 17);
00293         {
00294             // file size
00295             i = ftell(fp);
00296             fseek(fp, 0, SEEK_END);
00297             len = ftell(fp);
00298             fseek(fp, i, SEEK_SET);
00299         }
00300         sprintf(buf, "Content-Length: %d\r\n", len);
00301         send(cid, buf, strlen(buf));
00302         sprintf(buf, "Content-Type: %s\r\n", mimetype(file));
00303         send(cid, buf, strlen(buf));
00304         if (gshttpd->keepalive) {
00305             strcpy(buf, "Connection: Keep-Alive\r\n");
00306         } else {
00307             strcpy(buf, "Connection: close\r\n");
00308         }
00309         send(cid, buf, strlen(buf));
00310         strcpy(buf, "Server: GSwifi httpd\r\n");
00311         send(cid, buf, strlen(buf));
00312         send(cid, "\r\n", 2);
00313 
00314         for (;;) {
00315             i = fread(buf, sizeof(char), sizeof(buf), fp);
00316             if (i <= 0) break;
00317             if (! _gs_sock[cid].connect) break;
00318             send(cid, buf, i);
00319 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
00320             if (feof(fp)) break;
00321 #endif
00322         }
00323         fclose(fp);
00324         LOG("%d.%d.%d.%d ", _httpd[cid].host.getIp()[0], _httpd[cid].host.getIp()[1], _httpd[cid].host.getIp()[2], _httpd[cid].host.getIp()[3]);
00325         LOG("%s %s %d 200 %d\r\n", _httpd[cid].type == GSPROT_HTTPGET ? "GET" : "POST", _httpd[cid].uri, _httpd[cid].length, len);
00326         return 0;
00327     }
00328 
00329     send_httpd_error(cid, 404);
00330     return -1;
00331 }
00332 
00333 char *GSwifi::mimetype (char *file) {
00334     int i, j;
00335 
00336     DBG("<%s>\r\n", file);
00337     for (i = 0; i < MIMETABLE_NUM; i ++) {
00338         j = strlen(mimetable[i].ext);
00339         if (file[strlen(file) - j - 1] == '.' &&
00340           strnicmp(&file[strlen(file) - j], mimetable[i].ext, j) == NULL) {
00341             return (char*)mimetable[i].type;
00342         }
00343     }
00344     return (char*)mimetable[0].type;
00345 }
00346 
00347 int GSwifi::strnicmp (const char *p1, const char *p2, int n) {
00348     int i, r = -1;
00349     char c1, c2;
00350     
00351     for (i = 0; i < n; i ++) {
00352         c1 = (p1[i] >= 'a' && p1[i] <= 'z') ? p1[i] - ('a' - 'A'): p1[i];
00353         c2 = (p2[i] >= 'a' && p2[i] <= 'z') ? p2[i] - ('a' - 'A'): p2[i];
00354         r = c1 - c2;
00355         if (r) break;
00356     }
00357     return r;
00358 }
00359 
00360 void GSwifi::send_httpd_error (int cid, int err) {
00361     char buf[100], msg[30];
00362     
00363     switch (err) {
00364     case 400:
00365         strcpy(msg, "Bad Request");
00366         break;
00367     case 403:
00368         strcpy(msg, "Forbidden");
00369         break;
00370     case 404:
00371         strcpy(msg, "Not Found");
00372         break;
00373     case 500:
00374     default:
00375         strcpy(msg, "Internal Server Error");
00376         break;
00377     }
00378     DBG("httpd error: %d %d %s\r\n", cid, err, msg);
00379     
00380     sprintf(buf, "HTTP/1.1 %d %s\r\n", err, msg);
00381     send(cid, buf, strlen(buf));
00382     strcpy(buf, "Content-Type: text/html\r\n");
00383     send(cid, buf, strlen(buf));
00384     send(cid, "\r\n", 2);
00385 
00386     sprintf(buf, "<html><head><title>%d %s</title></head>\r\n", err, msg);
00387     send(cid, buf, strlen(buf));
00388     sprintf(buf, "<body><h1>%s</h1></body></html>\r\n", msg);
00389     send(cid, buf, strlen(buf));
00390     close(cid);
00391     LOG("%d.%d.%d.%d ", _httpd[cid].host.getIp()[0], _httpd[cid].host.getIp()[1], _httpd[cid].host.getIp()[2], _httpd[cid].host.getIp()[3]);
00392     LOG("%s %s %d %d -\r\n", _httpd[cid].type == GSPROT_HTTPGET ? "GET" : "POST", _httpd[cid].uri, _httpd[cid].length, err);
00393 }
00394 
00395 int GSwifi::attach_httpd (const char *uri, const char *dir) {
00396     if (_handler_count < HTTPD_HANDLE) {
00397         _handler[_handler_count].uri = (char*)malloc(strlen(uri) + 1);
00398         strcpy(_handler[_handler_count].uri, uri);
00399         _handler[_handler_count].dir = (char*)malloc(strlen(dir) + 1);
00400         strcpy(_handler[_handler_count].dir, dir);
00401         _handler[_handler_count].onHttpCgi = NULL;
00402         _handler_count ++;
00403         return 0;
00404     } else {
00405         return -1;
00406     }
00407 }
00408 
00409 int GSwifi::attach_httpd (const char *uri, onHttpdCgiFunc ponHttpCgi) {
00410     if (_handler_count < HTTPD_HANDLE) {
00411         _handler[_handler_count].uri = (char*)malloc(strlen(uri) + 1);
00412         strcpy(_handler[_handler_count].uri, uri);
00413         _handler[_handler_count].dir = NULL;
00414         _handler[_handler_count].onHttpCgi = ponHttpCgi;
00415         _handler_count ++;
00416         return 0;
00417     } else {
00418         return -1;
00419     }
00420 }
00421 
00422 #ifdef GS_ENABLE_WEBSOCKET
00423 void GSwifi::poll_websocket (int cid, int len) {
00424     int i, j, flg;
00425     unsigned char c;
00426 
00427     while (_gs_sock[cid].connect && (! _gs_sock[cid].data->isEmpty())) {
00428         flg = 0;
00429         // get 1 line
00430         for (j = 0; j < len; j ++) {
00431             _gs_sock[cid].data->dequeue((char*)&c);
00432 //            DBG("_%c", c);
00433 
00434             switch (_httpd[cid].mode) {
00435             case GSHTTPDMODE_WEBSOCKET:
00436                 if (_httpd[cid].len == 0) {
00437                     // flag
00438                     _httpd[cid].type = c & 0x0f;
00439                     _httpd[cid].websocket_flg = c << 8;
00440                     _httpd[cid].len ++;
00441                 } else
00442                 if (_httpd[cid].len == 1) {
00443                     // length 7bit
00444                     _httpd[cid].websocket_flg |= c;
00445                     _httpd[cid].length = c & 0x7f;
00446                     _httpd[cid].len ++;
00447                     if (_httpd[cid].length < 126) {
00448                         _httpd[cid].len = 0;
00449                         if (_httpd[cid].websocket_flg & 0x0080) {
00450                             _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET_MASK;
00451                         } else {
00452                             _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET_BODY;
00453                         }
00454                         DBG("ws length %d\r\n", _httpd[cid].length);
00455                     }
00456                 } else {
00457                     // length 16bit,64bit
00458                     if (_httpd[cid].len == 2) {
00459                         _httpd[cid].length = c;
00460                         _httpd[cid].len ++;
00461                     } else
00462                     if (_httpd[cid].len < 9 && (_httpd[cid].websocket_flg & 0x7f) == 127) {
00463                         // 64bit
00464                         _httpd[cid].length = (_httpd[cid].length << 8) | c;
00465                         _httpd[cid].len ++;
00466                     } else {
00467                         // end
00468                         _httpd[cid].length = (_httpd[cid].length << 8) | c;
00469                         _httpd[cid].len = 0;
00470                         if (_httpd[cid].websocket_flg & 0x0080) {
00471                             _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET_MASK;
00472                         } else {
00473                             _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET_BODY;
00474                         }
00475                         DBG("ws length2 %d\r\n", _httpd[cid].length);
00476                     }
00477                 }
00478                 break;
00479 
00480             case GSHTTPDMODE_WEBSOCKET_MASK:
00481                 // masking key
00482                 _httpd[cid].websocket_mask[_httpd[cid].len] = c;
00483                 _httpd[cid].len ++;
00484                 if (_httpd[cid].len >= 4) {
00485                     _httpd[cid].len = 0;
00486                     _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET_BODY;
00487                     DBG("ws mask\r\n");
00488                 }
00489                 break;
00490 
00491             case GSHTTPDMODE_WEBSOCKET_BODY:
00492                 // payload
00493                 if (_httpd[cid].len < HTTPD_BUF_SIZE - 1) {
00494                     if (_httpd[cid].websocket_flg & 0x0080) {
00495                         // un-mask
00496                         _httpd[cid].buf[_httpd[cid].len] = c ^ _httpd[cid].websocket_mask[_httpd[cid].len & 0x03]; 
00497                     } else {
00498                         _httpd[cid].buf[_httpd[cid].len] = c; 
00499                     }
00500                     _httpd[cid].len ++;
00501                 }
00502                 break;
00503             }
00504 
00505             if (_httpd[cid].mode == GSHTTPDMODE_WEBSOCKET_BODY && _httpd[cid].len >= _httpd[cid].length) {
00506                 flg = 1;
00507                 break;
00508             }
00509         }
00510         if (j >= len) return; // continue
00511         if (_httpd[cid].len < HTTPD_BUF_SIZE) {
00512             _httpd[cid].buf[_httpd[cid].len] = 0;
00513             DBG("websocket %d: (%d)\r\n", cid, _httpd[cid].len);
00514         }
00515 
00516         if (flg) {
00517             // websocket request
00518           DBG("ws type %d\r\n", _httpd[cid].type);
00519           switch (_httpd[cid].type) {
00520           case 0x00: // continuation
00521           case 0x01: // text
00522           case 0x02: // binary
00523             i = get_handler(_httpd[cid].uri);
00524             if (i >= 0) {
00525                 if (_handler[i].onHttpCgi) {
00526                     // cgi
00527                     _handler[i].onHttpCgi(cid, &_httpd[cid]);
00528                     LOG("%d.%d.%d.%d ", _httpd[cid].host.getIp()[0], _httpd[cid].host.getIp()[1], _httpd[cid].host.getIp()[2], _httpd[cid].host.getIp()[3]);
00529                     LOG("%s %s %d 200 -\r\n", "WEBSOCKET", _httpd[cid].uri, _httpd[cid].length);
00530                     flg = 1;
00531                 }
00532             }
00533             break;
00534 
00535           case 0x08: // close
00536             close(cid);
00537             break;
00538 
00539           case 0x09: // ping
00540             {
00541             char pong[_httpd[cid].len + 2];
00542             pong[0] = 0x8a;
00543             pong[1] = 0x04;
00544             memcpy(&pong[2], _httpd[cid].buf, _httpd[cid].len);
00545             send(cid, pong, _httpd[cid].len + 2);
00546             }
00547             break;
00548 
00549           case 0x0a: // pong
00550             break;
00551           }
00552           _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET;
00553           _httpd[cid].len = 0;
00554           _httpd[cid].length = 0;
00555         }
00556     } // while
00557 }
00558 
00559 void GSwifi::send_websocket_accept (int cid) {
00560     char buf[100], buf2[20];
00561     
00562     DBG("websocket accept: %d\r\n", cid);
00563 
00564     send(cid, "HTTP/1.1 101 Switching Protocols\r\n", 34);
00565     send(cid, "Upgrade: websocket\r\n", 20);
00566     send(cid, "Connection: Upgrade\r\n", 21);
00567 
00568     send(cid, "Sec-WebSocket-Accept: ", 22);
00569     strcpy(buf, _httpd[cid].websocket_key);
00570     strcat(buf, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
00571     sha1(buf, strlen(buf), buf2);
00572     base64encode(buf2, 20, buf, sizeof(buf));
00573     send(cid, buf, strlen(buf));
00574     send(cid, "\r\n", 2);
00575 //    send(cid, "Sec-WebSocket-Protocol: chat\r\n", 30);
00576     send(cid, "\r\n", 2);
00577     LOG("%d.%d.%d.%d ", _httpd[cid].host.getIp()[0], _httpd[cid].host.getIp()[1], _httpd[cid].host.getIp()[2], _httpd[cid].host.getIp()[3]);
00578     LOG("%s %s %d 101 - %s\r\n", _httpd[cid].type == GSPROT_HTTPGET ? "GET" : "POST", _httpd[cid].uri, _httpd[cid].length, buf);
00579 }
00580 #endif // GS_ENABLE_WEBSOCKET
00581 
00582 #endif