liveserver

simple live web server for local developments
git clone https://noulin.net/git/liveserver.git
Log | Files | Refs | README | LICENSE

commit d1833e88da92d72b30d2a827520ae7c11f335929
parent f9f4bc9c8a388f5f9313bb9810385546049f3643
Author: Remy Noulin <loader2x@gmail.com>
Date:   Mon, 13 Nov 2017 08:58:28 +0100

print server url at start

liveserver.c |   2 +
package.yml  |   2 +-
server.c     | 448 -----------------------------------------------------------
3 files changed, 3 insertions(+), 449 deletions(-)

Diffstat:
Mliveserver.c | 2++
Mpackage.yml | 2+-
Dserver.c | 448-------------------------------------------------------------------------------
3 files changed, 3 insertions(+), 449 deletions(-)

diff --git a/liveserver.c b/liveserver.c @@ -406,6 +406,8 @@ int main(int argc, char **argv) dirList = allocG(rtSmallArrayt); execO("find . -type d", dirList, NULL); + printf(GRN "http://0.0.0.0:%s/" RST "\nlog: server.log", portS); + if(fork() != 0) return 0; (void)signal(SIGCLD, SIG_IGN); diff --git a/package.yml b/package.yml @@ -1,6 +1,6 @@ --- name: liveserver - version: 0.0.3 + version: 0.0.4 description: "simple web server that reloads when file updates are detected" bin: ./liveserver.c repository: diff --git a/server.c b/server.c @@ -1,448 +0,0 @@ -#! /usr/bin/env sheepy - -/* - * project: miniweb - * author: Oscar Sanchez (oms1005@gmail.com) - * HTTP Server - * WORKS ON BROWSERS TOO! - * Inspired by IBM's nweb -*/ - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <errno.h> -#include <string.h> -#include <fcntl.h> -#include <signal.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include "inoty.h" - -#include "libsheepyObject.h" -#include "utilities.h" - -#define BUFSIZE 8096 -#define ERROR 42 -#define SORRY 43 -#define LOG 44 - -i64 waitTime = 0; - -smallArrayt *dirList; - -const char injected[] = "<!-- Code injected by live-server -->\n\ -<script type=\"text/javascript\">\n\ - // <![CDATA[ <-- For SVG support\n\ - if ('WebSocket' in window) {\n\ - (function() {\n\ - function refreshCSS() {\n\ - var sheets = [].slice.call(document.getElementsByTagName(\"link\"));\n\ - var head = document.getElementsByTagName(\"head\")[0];\n\ - for (var i = 0; i < sheets.length; ++i) {\n\ - var elem = sheets[i];\n\ - head.removeChild(elem);\n\ - var rel = elem.rel;\n\ - if (elem.href && typeof rel != \"string\" || rel.length == 0 || rel.toLowerCase() == \"stylesheet\") {\n\ - var url = elem.href.replace(/(&|\\?)_cacheOverride=\\d+/, '');\n\ - elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf());\n\ - }\n\ - head.appendChild(elem);\n\ - }\n\ - }\n\ - var protocol = window.location.protocol === 'http:' ? 'ws://' : 'wss://';\n\ - var address = protocol + window.location.host + window.location.pathname + '/ws';\n\ - var socket = new WebSocket(address);\n\ - socket.onmessage = function(msg) {\n\ - if (msg.data == 'reload') window.location.reload();\n\ - else if (msg.data == 'refreshcss') refreshCSS();\n\ - };\n\ - console.log('Live reload enabled.');\n\ - })();\n\ - }\n\ - // ]]>\n\ -</script>\n\ -"; - -struct { - char *ext; - char *filetype; -} extensions [] = { - {"gif", "image/gif" }, - {"jpg", "image/jpeg"}, - {"jpeg","image/jpeg"}, - {"png", "image/png" }, - {"zip", "image/zip" }, - {"gz", "image/gz" }, - {"tar", "image/tar" }, - {"htm", "text/html" }, - {"html","text/html" }, - {"php", "image/php" }, - {"cgi", "text/cgi" }, - {"asp", "text/asp" }, - {"jsp", "image/jsp" }, - {"xml", "text/xml" }, - {"js", "text/js" }, - {"css", "text/css" }, - {"//ws", "websocket"}, - - {0,0} }; - -#define WEBSOCKET_SERVER_RESPONSE "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - -#define WEBSOCK_PACKAGE_NAME "s" -#define WEBSOCK_PACKAGE_VERSION "0" - -void slog(int type, char *s1, char *s2, int num) -{ - int fd ; - char logbuffer[BUFSIZE*2]; - - switch (type) { - case ERROR: (void)sprintf(logbuffer,"ERROR: %s:%s Errno=%d exiting pid=%d",s1, s2, errno,getpid()); break; - case SORRY: - (void)sprintf(logbuffer, "<HTML><BODY><H1>Web Server Sorry: %s %s</H1></BODY></HTML>\r\n", s1, s2); - (void)write(num,logbuffer,strlen(logbuffer)); - (void)sprintf(logbuffer,"SORRY: %s:%s",s1, s2); - break; - case LOG: (void)sprintf(logbuffer," INFO: %s:%s:%d",s1, s2,num); break; - } - - if((fd = open("server.log", O_CREAT| O_WRONLY | O_APPEND,0644)) >= 0) { - (void)write(fd,logbuffer,strlen(logbuffer)); - (void)write(fd,"\n",1); - (void)close(fd); - } - if(type == ERROR || type == SORRY) exit(3); -} - -void web(int fd, int hit) -{ - int j, file_fd, buflen, len; - long i, ret; - char * fstr; - static char buffer[BUFSIZE+1]; - - ret =read(fd,buffer,BUFSIZE); - if(ret == 0 || ret == -1) { - slog(SORRY,"failed to read browser request","",fd); - } - if(ret > 0 && ret < BUFSIZE) - buffer[ret]=0; - else buffer[0]=0; - - smallStringt *reqS = allocG(buffer); - smallArrayt *req = splitG(reqS, "\r\n"); - char *clientKey = NULL; - char *origin = NULL; - char *protocol = NULL; // protocol not parsed, firefox 52 doesnt send it - forEachSmallArray(req, S) { - castS(s,S); - if (startsWithG(s, "Sec-WebSocket-Key:")) { - clientKey = ssGet(s) + 19; - } - if(startsWithSG(s, "Origin:")) { - origin = ssGet(s) + 8; - } - finishG(s); - } - - for(i=0;i<ret;i++) - if(buffer[i] == '\r' || buffer[i] == '\n') - buffer[i]='*'; - slog(LOG,"request",buffer,hit); - - if( strncmp(buffer,"GET ",4) && strncmp(buffer,"get ",4) ) - slog(SORRY,"Only simple GET operation supported",buffer,fd); - - for(i=4;i<BUFSIZE;i++) { - if(buffer[i] == ' ') { - buffer[i] = 0; - break; - } - } - - for(j=0;j<i-1;j++) - if(buffer[j] == '.' && buffer[j+1] == '.') - slog(SORRY,"Parent directory (..) path names not supported",buffer,fd); - - if( !strncmp(&buffer[0],"GET /\0",6) || !strncmp(&buffer[0],"get /\0",6) ) - (void)strcpy(buffer,"GET /index.html"); - - bool isHTML = false; - bool isWebsocket = false; - - buflen=strlen(buffer); - fstr = (char *)0; - for(i=0;extensions[i].ext != 0;i++) { - len = strlen(extensions[i].ext); - if( !strncmp(&buffer[buflen-len], extensions[i].ext, len)) { - fstr =extensions[i].filetype; - if (startsWithG(extensions[i].ext, "htm")) isHTML = true; - else if (eqS(extensions[i].ext, "//ws")) isWebsocket = true; - break; - } - } - if(fstr == 0) slog(SORRY,"file extension type not supported",buffer,fd); - - if (isWebsocket) { - // WEBSOCKET HANDLING - - slog(LOG,"websocket","detected",hit); - slog(LOG,"key",clientKey,hit); - - SHA1Context shactx; - SHA1Reset(&shactx); - - char buf[1024]; - sprintf(buf, "%s%s", clientKey, WEBSOCKET_SERVER_RESPONSE); - - SHA1Input(&shactx, (unsigned char *) buf, strlen(buf)); - SHA1Result(&shactx); - - char sha1buf[45]; - unsigned char sha1mac[20]; - - sprintf(sha1buf, "%08x%08x%08x%08x%08x", shactx.Message_Digest[0], - shactx.Message_Digest[1], shactx.Message_Digest[2], - shactx.Message_Digest[3], shactx.Message_Digest[4]); - - for (i16 n = 0; n < (strlen(sha1buf) / 2); n++) { - sscanf(sha1buf + (n * 2), "%02hhx", sha1mac + n); - } - - char base64buf[256]; - base64_encode(sha1mac, 20, base64buf, 256); - memset(buf, 0, 1024); - snprintf(buf, 1024, "HTTP/1.1 101 Switching Protocols\r\n" - "Server: %s/%s\r\n" - "Upgrade: websocket\r\n" - "%s%s%s" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: %s\r\n%s%s" - "Access-Control-Allow-Headers: content-type\r\n\r\n", WEBSOCK_PACKAGE_NAME, - WEBSOCK_PACKAGE_VERSION, origin ? "Access-Control-Allow-Origin: " : "", origin ? origin : "", origin ? "\r\n" : "", base64buf, protocol ? protocol : "", protocol ? "\r\n" : ""); - - (void)write(fd,buf,strlen(buf)); - - // INOTIFY SETUP - - // wait for changes and reload page - enum {yes, no}; - i8 foundRelevantUpdate = no; - while (foundRelevantUpdate == no) { - // creating the INOTIFY instance - int length; - char ibuffer[EVENT_BUF_LEN]; - int ifd = inotiFy_init(); - if (ifd == -1) { - perror("inotiFy_init error: "); - } - - int wd; // watch descriptor - - forEachSmallArray(dirList, E) { - castS(e, E); - wd = inotiFy_add_watch( ifd, ssGet(e), IN_CREATE | IN_DELETE | IN_MODIFY); - finishG(e); - } - - // read to determine the event change happens on “.” directory. Actually this read blocks until the change event occurs - length = read( ifd, ibuffer, EVENT_BUF_LEN );; - - if (length == -1) { - perror("read error: "); - } - - u32 i = 0; - // actually read return the list of change events happens. Here, read the change event one by one and process it accordingly. - while (( i < length )) { - struct inotiFy_event *event = ( struct inotiFy_event * ) &ibuffer[ i ]; - - for(i=0;extensions[i].ext != 0;i++) { - if (endsWithG(event->name, extensions[i].ext)) { - if ((event->mask & IN_CREATE) || (event->mask & IN_DELETE) || (event->mask & IN_MODIFY)) { - foundRelevantUpdate = yes; - } - } - } - - i += EVENT_SIZE + event->len; - } - - - // removing the “/tmp” directory from the watch list. - inotiFy_rm_watch( ifd, wd ); - - // closing the INOTIFY instance - close(ifd); - } - - if (waitTime > 0) { - // wait before reloading - sleep(waitTime); - } - - // WEBSOCKET SEND RELOAD TO CLIENT - - // send reload - u32 fin = 1; - u32 op = 1; - u32 plen = 6; - u32 blen = plen+2; - - buf[0] = (1 << 7) + fin; // 0x81 - buf[1] = plen; - /* uint16_t *b = (uint16_t*)buf; */ - /* *b = (plen << 9) + (op << 4) + fin; */ - strcpy(buf+2, "reload"); - - (void)write(fd,buf,blen); - } else { - - // SERVE REGULAR HTTP REQUESTS - - char *serveFile = &buffer[5]; - - if (isHTML) { - size_t endBodyAt = 0; - createAllocateSmallArray(f); - readFileG(f, &buffer[5]); - enumerateSmallArray(f, S, n) { - castS(s, S); - if (hasG(lowerG(s), "</body>")) { - endBodyAt = n; - finishG(s); - break; - } - finishG(s); - } - injectSG(f, endBodyAt, injected); - writeFileG(f, "a"); - terminateG(f); - serveFile = "a"; - } - - if(( file_fd = open(serveFile,O_RDONLY)) == -1) - slog(SORRY, "failed to open file",serveFile,fd); - - slog(LOG,"SEND",serveFile,hit); - - (void)sprintf(buffer,"HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n", fstr); - (void)write(fd,buffer,strlen(buffer)); - - while ((ret = read(file_fd, buffer, BUFSIZE)) > 0 ) { - (void)write(fd,buffer,ret); - } - } - - terminateManyG(reqS, req); - -#ifdef LINUX - sleep(1); -#endif - exit(1); -} - - -int main(int argc, char **argv) -{ - int i, port, pid, listenfd, socketfd, hit; - size_t length; - static struct sockaddr_in cli_addr; - static struct sockaddr_in serv_addr; - char *portS; - char *dir = NULL; - - // defaults - port = 8080; - portS = "8080"; - - forEachS(argv, a) { - if (startsWithG(a, "-port=")) { - portS = a+6; - port = parseInt(portS); - } - else if (startsWithG(a, "-path=")) { - dir = a+6; - } - else if (startsWithG(a, "-wait=")) { - waitTime = parseInt(a+6); - } - else if (startsWithG(a, "--help") or startsWithG(a, "-h") or startsWithG(a, "-?")) { - printf("usage: server [-port=80] [-path=server_directory] [-wait=1]\n" - "\twait unit is second.\n" - "\tExample: server -port=8080 -path=./ -wait=1\n\n" - "\tOnly Supports:"); - for(i=0;extensions[i].ext != 0;i++) - (void)printf(" %s",extensions[i].ext); - - (void)printf("\n\tNot Supported: directories / /etc /bin /lib /tmp /usr /dev /sbin \n" - ); - XSUCCESS - } - } - - if (dir) { - if( !strncmp(dir,"/" ,2 ) || !strncmp(dir,"/etc", 5 ) || - !strncmp(dir,"/bin",5 ) || !strncmp(dir,"/lib", 5 ) || - !strncmp(dir,"/tmp",5 ) || !strncmp(dir,"/usr", 5 ) || - !strncmp(dir,"/dev",5 ) || !strncmp(dir,"/sbin",6) ){ - (void)printf("ERROR: Bad top directory %s, see server -?\n",dir); - exit(3); - } - if(chdir(dir) == -1){ - (void)printf("ERROR: Can't Change to directory %s\n",dir); - exit(4); - } - } - - dirList = allocG(rtSmallArrayt); - execO("find . -type d", dirList, NULL); - - if(fork() != 0) - return 0; - (void)signal(SIGCLD, SIG_IGN); - (void)signal(SIGHUP, SIG_IGN); - for(i=0;i<32;i++) - (void)close(i); - (void)setpgrp(); - - slog(LOG,"http server starting",portS,getpid()); - - if((listenfd = socket(AF_INET, SOCK_STREAM,0)) <0) - slog(ERROR, "system call","socket",0); - - if(port < 0 || port >60000) - slog(ERROR,"Invalid port number try [1,60000]",portS,0); - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); - serv_addr.sin_port = htons(port); - if(bind(listenfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr)) <0) - slog(ERROR,"system call","bind",0); - if( listen(listenfd,64) <0) - slog(ERROR,"system call","listen",0); - - for(hit=1; ;hit++) { - length = sizeof(cli_addr); - if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, (socklen_t *)&length)) < 0) - slog(ERROR,"system call","accept",0); - - if((pid = fork()) < 0) { - slog(ERROR,"system call","fork",0); - } - else { - if(pid == 0) { - (void)close(listenfd); - web(socketfd,hit); - } else { - (void)close(socketfd); - } - } - } - - terminateG(dirList); - -}