// asynchronous HTTP server in EEL function httpd.init(port, maxcon, table, stringoffs, wwwroot) global() ( this.maxcon = maxcon; this.tab = table; this.port = port; this.stringtab = stringoffs; this.string_recsz = 2; this.recsz = 3; #this.wwwroot = wwwroot; ); function httpd.getrecval(conid, parm) global() ( conid >= 0 && conid < this.maxcon ? ( parm == 'sock' ? this.tab + conid*this.recsz + 0 : // socket parm == 'st' ? this.tab + conid*this.recsz + 1 : // state parm == 'fh' ? this.tab + conid*this.recsz + 2 : // file handle 0) : 0 ); function httpd.set(conid, parm, value) local(x) global() ( x = httpd.getrecval(conid,parm); x>=0 ? x[]=value; ); function httpd.get(conid, parm) local(x) global() ( x=httpd.getrecval(conid,parm); x>=0?x[]; ); function httpd.getbuf(conid, w) global() ( conid >=0 && conid < this.maxcon ? this.stringtab + this.string_recsz*conid + w : -1; ); function httpd.close(conid) global() ( conid >=0 && conid < this.maxcon ? ( tcp_close(httpd.get(conid,'sock')); fclose(httpd.get(conid,'fh')); httpd.set(conid,'sock',0); httpd.set(conid,'fh',0); ); ); function httpd.file_for_req(req, response) local(fn,fp) global() ( !strcmp(req,"") || !strcmp(req,"/") ? req = "/index.html"; str_getchar(req,0) == '/' && !match("*..*",req) && (fp=fopen(fn = strcat(#=#this.wwwroot,req),"rb")) > 0 ? ( fseek(fp,0,1); printf("GET %s -- 200: %s\n",req,fn); strcat(response,sprintf(#, "HTTP/1.1 200 OK\r\n" "Content-length: %d\r\n" "Content-type: text/html\r\n" "\r\n", ftell(fp))); fseek(fp,0,-1); fp; ) : ( printf("GET %s -- 404\n",req); fn = "The file specified could not be found."; strcat(response,sprintf(#, "HTTP/1.1 404 NOT FOUND\r\n" "Content-length: %d\r\n" "Content-type: text/plain\r\n" "\r\n" "%s", strlen(fn),fn)); 0; ); ); // run input and output buffers function httpd.runcon(idx) local(sock,x,str,rv,buffers) global() ( rv=0; sock = httpd.get(idx,'sock'); sock > 0 ? ( x=tcp_recv(sock,str=#); x<0 ? ( httpd.close(idx); ) : ( buffers=httpd.getbuf(idx,0); x>0 ? strcat(buffers,str); rv+=x; strlen(buffers+1)>0 ? ( (x=tcp_send(sock,buffers+1)) > 0 ? ( rv+=1; str_delsub(buffers+1.1,0,x); ); ); ); ); rv; ); function httpd.run() local(sock, x, i, str, rv, t,hdrs,httpdver) global() ( str=#; hdrs=#; rv=0; sock=tcp_listen(this.port,"",str); sock > 0 ? ( i=0; while (i < this.maxcon && httpd.get(i,'sock')) ( i += 1; ); i < this.maxcon ? ( tcp_set_block(sock,0); httpd.set(i,'sock',sock); httpd.set(i,'st',0); x=0; loop(this.string_recsz, strcpy(httpd.getbuf(i,x), ""); x+=1; ); printf("httpd.run(): connection from '%s'\n",str); rv+=1; ) : ( printf("httpd.run(): dropping connect from '%s', slots full\n",str); tcp_close(x); ); ); i=0; while (i < this.maxcon) ( this.httpd.runcon(i) ? ( t = httpd.getbuf(i,0); this.httpd.get(i,'st') == 0 ? ( match("GET %S HTTP/1.%1d%S",t,str,httpdver,hdrs) && str_getchar(hdrs,0)=='\r' && str_getchar(hdrs,1)=='\n' && !strcmp(strcpy_substr(#,hdrs,-4),"\r\n\r\n") ? ( httpd.set(i,'st',1 | ((httpdver==0 || matchi("\r\nConnection:*close\r\n",hdrs))?2:0) ); httpd.set(i,'fh',httpd.file_for_req(str,t+1)); ); ); httpd.get(i,'fh') && !strlen(t+1) ? fread(httpd.get(i,'fh'),t+1,4096); httpd.get(i,'st') == 3 ? ( strlen(t+1) == 0 ? ( httpd.close(i); printf("sent data, closing\n"); ); ); rv+=1; ); i+=1; ); rv; ); argc != 3 || strlen(argv[1])<1 || !match("%d",argv[2],port) ? ( printf("Usage: \n\t%s www_root port\n",argv[0]); ) : ( srv.httpd.init(port, 100, 100000, 10000, argv[1]); // string [conidx] = send buffe while(1) ( srv.httpd.run() || Sleep(10); ); );