Try   HackMD

HTTP 1.0 web server+CGI-sample code

tags: linux network programming http CGI

Author: CrazyMonkey
E-mail: kccddb@gmail.com

Date: 20230222
Copyright: CC BY-NC-SA

現今 HTTP 除了 HTTP 1.0 與 HTTP 1.1 的差別外 還有一重要的改進: HTTP persistent connection (HTTP keep-alive)

HTTP persistent connection, is an instruction that allows a single TCP connection to remain open for multiple HTTP requests and responses.

您可以使用 Wireshark 幫助自己了解此現象!

詳見:
RFC 2616 Hypertext Transfer Protocol HTTP/1.1

RFC 2616:

Persistent HTTP connections have a number of advantages:

​​​​  - By opening and closing fewer TCP connections, CPU time is saved
​​​​    in routers and hosts (clients, servers, proxies, gateways,
​​​​    tunnels, or caches), and memory used for TCP protocol control
​​​​    blocks can be saved in hosts.

​​​​  - HTTP requests and responses can be pipelined on a connection.
​​​​    Pipelining allows a client to make multiple requests without
​​​​    waiting for each response, allowing a single TCP connection to
​​​​    be used much more efficiently, with much lower elapsed time.

​​​​  - Network congestion is reduced by reducing the number of packets
​​​​    caused by TCP opens, and by allowing TCP sufficient time to
​​​​    determine the congestion state of the network.

​​​​  - Latency on subsequent requests is reduced since there is no time
​​​​    spent in TCP's connection opening handshake.

​​​​  - HTTP can evolve more gracefully, since errors can be reported
​​​​    without the penalty of closing the TCP connection. Clients using
​​​​    future versions of HTTP might optimistically try a new feature,
​​​​    but if communicating with an older server, retry with old
​​​​    semantics after an error is reported.

*

Front-end/Back-end

此例子 這主要用 fork() 完成也沒有處理 timeout! 經不起壓力測試的!! demo 學習用 !
練習 TRY IT, FIXME

int dup2(int oldfd, int newfd);

shttpcgi.c
gcc shttpcgi.c -o shttpcgi

shttpcgi 9000

TEST CGI:
shttpcgi 9000 1

Author:
WhoAmI 2019

以下連結請務必研讀(引用自 stackoverflow)

You can use the SO_RCVTIMEO and SO_SNDTIMEO socket options to set timeouts for any socket operations.

linux man dup2
linux man execvp
execve - execute program
linux man setenv

注意 backlog 的意義:
The backlog argument defines the maximum length to which the queue of pending connections for sockfd may grow.

TCP SYN flood, server 會出問題, Why?

int listen(int sockfd, int backlog);

很重要的理解 網路程式設計 的例子, 學以致用, 一定要實作

當 process 執行時 事先 open 了 stdin (0), stdout (1), stderr (2)

Please study Probability, Statistics, OS and Algorithms.

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <signal.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <sys/stat.h> #include <fcntl.h> #define debug001(x) #define debug000(x) x //CHECK THIS #define FORK 1 #define CYGWIN 1 #define READTIMEOUT 3 #define CANNOTREAD -110 //gcc httpcgi.c -o httpcgi #define debug(x) x #define HTTPHEADERLEN 1024 void PANIC(char* msg); #define PANIC(msg) { perror(msg); exit(EXIT_FAILURE); } static void sig_handler(int signo) { pid_t pid; int stat; switch(signo) { case SIGCHLD: printf("SIGCHLD=%d\n",signo); pid=waitpid(0,&stat,WNOHANG); break; case SIGINT: printf("SIGINT=%d\n",signo); break; case SIGALRM: printf("Got SIGALRM=%d, exit!\n",signo); exit( EXIT_FAILURE); break; default: printf("signal=%d\n",signo); } return; } static off_t getfilesize(char *fname) { struct stat sb; /* off_t: total size, in bytes */ //%lld long long int if (stat(fname, &sb) == -1) { perror("stat"); return -1; } debug(printf("File size:%lld bytes\n",(long long) sb.st_size)); return sb.st_size; } char *readfile(char *fname, int *size) { off_t ssize; char *mem; int fd; int n; mem=NULL; ssize=getfilesize(fname); if(ssize>0) { fd=open(fname, O_RDONLY); if(fd>0) { mem=malloc(ssize); if(mem) { n= read(fd,mem,ssize); if(n>0) { *size=n; } else { *size=0; } close(fd); return mem; } else { close(fd); } } else { printf("Cannot open %s size=%lld",fname,ssize); //%lld long long int } }else{ printf("Cannot find %s\n",fname); return CANNOTREAD; } return NULL; } static void pdebug(char *argv[]) { int i; for(i=0;argv[i]!=NULL;i++) { printf("%s \n",argv[i]); } } static void http( struct sockaddr_in *client_addr ) { char buffer[1512]; char port[10]; int pid; #if CYGWIN //char *argv[3]={"./findp.exe","./findp.exe",0}; char *argv[3]={"ls","-l",0}; #else char *argv[3]={"./cgi","./cgi",0}; #endif #if DEBUG read(0,buffer,sizeof(buffer)); printf("%s\n",buffer); #endif setenv("REMOTE_ADDR", inet_ntoa(client_addr->sin_addr), 1); sprintf(port,"%d",ntohs(client_addr->sin_port)); setenv("REMOTE_PORT", port, 1); #if DEBUG pdebug(argv); #endif pid=getpid(); printf("My pid is %d\n",pid); if(pid>0) { printf("execvp...<BR>\n"); pdebug(argv); execvp(argv[0], argv); } else { printf("This is the new child(pid=%d) <BR>\n",getpid()); exit(0); } } static int httpResopnseBody(int fd) { char *body="<html><body><h1>It works!</h1></body></html>"; int n; //FIXME: Locale n=write(fd,body,strlen(body)); return n; } static int httpResponse(int fd) { int n; char *header="HTTP/1.1 200 OK\nContent-Type: text/html; charset=UTF-8\r\n\r\n"; //FIXME: Locale n=write(fd,header,strlen(header));//write header n=httpResopnseBody(fd); return n; } static ssize_t readln(int sockfd, char *buf,size_t len, int time_out) { int n; fd_set readfds; struct timeval tval; ssize_t total; int ret; char *pc; total=0; FD_ZERO(&readfds); do { FD_SET(sockfd, &readfds); tval.tv_sec = time_out; tval.tv_usec = 0; if(time_out<=0) { if(( n = select(sockfd+1, &readfds, NULL, NULL, NULL))<0)return -1; } else { if ( (n = select(sockfd+1, &readfds, NULL, NULL, time_out ? &tval : NULL)) == 0) { errno = ETIMEDOUT; printf("Read timeout!\n"); return(-1); } } ret=read(sockfd,buf+total,1); //Notice that '\n' pc= buf+total; if(ret>0) { total+=ret; if(*pc=='\n')break; } else if(ret==0) { //eof break; } else { return -1; } } while(total<len); if(total<len)buf[total]=0; else buf[len-1]=0; return total; } static int dump_http(int fd) { //ssize_t read(int fd, void *buf, size_t count); char buf[HTTPHEADERLEN]; ssize_t n; size_t count; char *mem; int size; int line=0; char *fname,*p; char *header_html="HTTP/1.1 200 OK\nContent-Type: text/html; charset=UTF-8\r\n\r\n"; char *header_jpg="HTTP/1.1 200 OK\nContent-Type: image/jpeg; charset=UTF-8\r\n\r\n"; char *header; count=sizeof(buf)-1; //FIXME: without timeout do { n=readln(fd,buf, count,READTIMEOUT); if(n>0) { buf[n]=0; //FIXME: CHECK THIS debug001(printf("[%d][%s]",line,buf)); debug000(printf("%s",buf)); if(line==0) { //fname=&buf[1]; //case 0: files p=strchr(buf,' '); if(p)fname=p+2; p=strchr(fname,' '); if(p)*p=0; debug001(printf("Read %s\n",fname)); header=header_html; if(strstr(fname,".jpg"))header=header_jpg; if(strstr(fname,".png"))header=header_jpg; mem=readfile(fname,&size); if(mem) { debug001(printf("Write %d bytes to %d...\n",size,fd)); write(fd,header,strlen(header)); write(fd,mem,size); free(mem); }else{ //cannot read file return CANNOTREAD; } } //case 1: open connection //----------------------------------------------------- //---------------------------------------------------------- line++; } else return n; } while(n>0); return n; } static int process_http(int fd) { int httpret=0; httpret=dump_http(fd); httpret=httpResponse(fd); return httpret; }//process_http int main(int argc, char *argv[]) { int sd; int port; int new_fd; int len; int state; int pid; int httpret; int size; char *fname="play.txt"; char *mem; int docgi=0; struct sockaddr_in server_addr,client_addr; if(argc>1)port=atoi(argv[1]); else port=80; if(argc==3)docgi=atoi(argv[2]); signal (SIGCHLD, sig_handler); //signal (SIGINT,sig_handler); //TRY IT! // signal (SIGALRM,sig_handler);//TRY IT! sd = socket(PF_INET, SOCK_STREAM, 0); memset(&server_addr,0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); server_addr.sin_addr.s_addr = INADDR_ANY; if ( bind(sd, (struct sockaddr*)&server_addr, sizeof(server_addr)) != 0 ) PANIC("Bind"); if ( listen(sd, 20) != 0 ) PANIC("Listen"); printf("Start server at port %d id=%d...",port,sd); while (1) { len = sizeof(client_addr); new_fd = accept(sd, (struct sockaddr*)&client_addr, &len); printf("%d,Client from %s:%d\n", new_fd,inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); if ( new_fd < 0 ) perror("Accept"); else { pid=fork(); if ( pid==0 ) { //child close(sd); httpret=process_http(new_fd); if(httpret<=0 || docgi>0) { dup2(new_fd, 0); dup2(new_fd, 1); dup2(new_fd, 2); close(new_fd); http( &client_addr ); wait(&state); } exit(EXIT_SUCCESS ); } close(new_fd); } } exit(EXIT_SUCCESS ); }

cgi.c

#include <stdio.h> #include <stdlib.h> #include <time.h> #define QUERY_STRING getenv("QUERY_STRING") #define CONTENT_TYPE getenv("CONTENT_TYPE") #define CONTENT_LENGTH getenv("CONTENT_LENGTH") #define REQUEST_METHOD getenv("REQUEST_METHOD") #define REMOTE_HOST getenv("REMOTE_HOST") #define REMOTE_ADDR getenv("REMOTE_ADDR") #define REMOTE_PORT getenv("REMOTE_PORT") print_environment() { printf("<B>REQUEST_METHOD:</B> %s<BR>\n",REQUEST_METHOD); printf("<B>CONTENT_TYPE:</B> %s<BR>\n",CONTENT_TYPE); printf("<B>CONTENT_LENGTH:</B> %s<BR>\n",CONTENT_LENGTH); printf("<B>QUERY_STRING:</B> %s<BR>\n",QUERY_STRING); printf("<B>REMOTE_HOST:</B> %s<BR>\n",REMOTE_HOST); printf("<B>REMOTE_ADDR:</B> %s<BR>\n",REMOTE_ADDR); } static char *header="HTTP/1.1 200 OK\nContent-Type: text/html; charset=UTF-8\r\n\r\n"; int main(int argc, char* argv[]) { time_t t; t=time(NULL); printf("%s",header); printf("<html><body>"); printf("<BR>Hello CGI!<BR> %s <BR><p style='color:blue;'>[Your IP:PORT %s:%s]</p><BR>",ctime(&t ),REMOTE_ADDR, REMOTE_PORT); print_environment(); printf("</body></html>\n\n"); }

Read Around:
Show the environment variables of a running process in Linux

How to Set Environment Variables in Linux

Environment Variables in Linux/Unix

The /proc Filesystem

e.g.,

kclai@ubuntu16:/proc/1867$ sudo cat environ
[sudo] password for kclai:
XDG_SESSION_ID=3SHELL=/bin/bashSSH_CLIENT=192.168.1.10 5376 22USER=kclaiMAIL=/var/mail/kclaiPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/binPWD=/home/kclaiLANG=en_US.UTF-8SHLVL=1HOME=/home/kclaiLOGNAME=kclaiSSH_CONNECTION=192.168.1.10 5376 192.168.1.31 22XDG_RUNTIME_DIR=/run/user/1000_=/usr/lib/openssh/sftp-serverkclai@ubuntu16:/proc/1867$


Homewok 實作與思考的問題

  1. 練習 Wireshark 觀察 HTTP 1.0 與 1.1 的不同
dup2(new_fd, 0); dup2(new_fd, 1); dup2(new_fd, 2); close(new_fd);

Why?  I/O redirection 運用 Linux system call 的實作方法 (redirect, pipe, …)

  1. 設計一 http server 可以 讓 user 由 browser 看到 文字與圖片
  2. HTTP 中 Content-Length 有何用途? Why?
  3. CGI 有何實務上的問題? 如何解決? (HINT: FastCGI)
  4. HTTP keep-alive, Why? 與 TCP keep-alive 有何不同?
  5. 如何取得 HTTP GET 的參數?

附註:
因圖中有 AJAX(Asynchronous JavaScript and XML)

盡可能 使用 AJAX POST 不要用傳統 FORM submit!

AJAX: Update a web page without reloading the page

盡可能 用 CSS

有些 夾雜 格式 於 HTML 中 方便學習, 但卻是不當的方法
不要期待 團隊裡成員 都懂 Javascript, PHP. 例如 美工可能不懂~

這是故意設計的例子把 callback, getElementsByName 加入

AJAX POST, callback and getElementsByName

<!DOCTYPE html> <!--Author: WhoAmI --> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <style> body { background-color: lightblue; } .redButton { background-color:red; } </style> </head> <body> <h2>盡可能 使用 AJAX POST 不要用傳統 FORM submit!</h2> <h2>AJAX POST, callback and getElementsByName (call tdemo.php)</h2> First Name: <input name="tName" type="text" value="Linux"><br> First Name: <input name="tName" type="text" value="John"><br> city: <input name="cName" type="text" value="taichung"><br> city : <input name="cName" type="text" value="taipei"> <button onclick="searchdb()" class="redButton" >AJAX POST</button> <!-- Display Server Response --> <div id="result"></div> <script> //AJAX //callback function function myCallback(xmldoc){ document.getElementById("result").innerHTML=xmldoc.responseText; } function myPost(url, data, callback) { var xmlDoc = new XMLHttpRequest(); xmlDoc.open('POST', url, true); xmlDoc.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xmlDoc.onreadystatechange = function() { if (xmlDoc.readyState === 4 && xmlDoc.status === 200) { callback(xmlDoc); } } xmlDoc.send(data); } //setup POST data function searchdb() { //getElementsByName():returns a collection of all elements ( NodeList ) list=document.getElementsByName("tName"); console.error(list); var tvalue=[]; list.forEach( function(currentValue, currentIndex, listObj) { tvalue.push(currentValue.value); console.error(tvalue+' ::'+currentValue + ', ' + currentIndex + ', ' + this); }, 'myThisArg' ); list=document.getElementsByName("cName"); console.error(list); var cvalue=[]; list.forEach( function(currentValue, currentIndex, listObj) { cvalue.push(currentValue.value); console.error(cvalue+' ::'+currentValue + ', ' + currentIndex + ', ' + this); }, 'myThisArg' ); query='tName='+tvalue+'&cName='+cvalue; //write your jdemo.php myPost('tdemo.php',query,myCallback); } </script> </body> </html>

tdemo.php

<?php if(isset($_POST["tName"])){ $tName=$_POST["tName"]; echo "Input:<br>"; print_r($tName); } echo "<br>"; if(isset($_POST["cName"])){ $cName=$_POST["cName"]; echo "Select:<br>"; print_r($cName); }

JavaScript

定義 class 方便使用 CSS
jelems-1.0.js

function crSelect(name,array,tarray=null,myclass="default-class"){
 
var selectList = document.createElement("select");
selectList.className = myclass;
selectList.name=name;


//Create and append the options
for (var i = 0; i < array.length; i++) {
    var option = document.createElement("option");
    option.value = array[i];
    if(tarray===null)
         option.text = array[i];
    else  option.text = tarray[i];     
    selectList.appendChild(option);
}
return selectList;
}

  function crBtn(name,click=null,myclass="default-class")
 {
  var e=document.createElement("INPUT");
  e.setAttribute("type","button");
  e.className=myclass;
  e.value=name;
   if(click!==null)e.addEventListener("click",click);
  return e;
  
 }
 
 
 function crImg(src,click=null,myclass="default-class")
 {
  var e=document.createElement("INPUT");
  e.setAttribute("type","image");
  
  e.src=src; //"img/enter.png";
   if(click!==null)e.addEventListener("click",click);
  
  e.className=myclass;
  return e;
  
 }
 function createImage(src,click=null,myclass="default-class"){

  var node = document.createElement('img');

            node.src = src;

            node.className=myclass;
           if(click!==null)node.addEventListener("click",click);
            return node;

 

}
 function crList(click=null,myclass="default-class"){
  var data="item 1 click it!";
  var node = document.createElement('li');
  node.appendChild(document.createTextNode(data));
   if(click!==null)node.addEventListener("click",click);
  node.className=myclass;
  return node;
 
 }
  function crLists(items,click=null,myclass="default-class"){
  
  var ul = document.createElement('ul');
  
  ul.setAttribute("class", "lists");
  items.forEach((data)=>{
   let li=document.createElement('li');
   li.className=myclass;

  if( data.nodeType == Node.ELEMENT_NODE){
      li.appendChild(data);
   
  }else {
   
   if(typeof data ==="string"){
                let isimg= data.match(/\.(jpeg|jpg|png|gif|bmp)/i);
                if(isimg){
                    //automatic convert to image
                    let img=createImage(data);
                    li.appendChild(img);
                    
                }else
                   li.appendChild(document.createTextNode(data));
    }else li.appendChild(document.createTextNode(data));
  }
   //li.appendChild(document.createTextNode(data));
   
 //  console.warn(ul);
   if(click!==null)li.addEventListener("click",click);
   ul.appendChild(li);
    
  });
  ul.className=myclass;
  return ul;
 
 }

 //creatre a TR via array
 function crTR(fields,click=null,myclass="default-class"){
   let td;
   
   let tr = document.createElement('tr');
   fields.forEach((field)=>{
       td=document.createElement('td');
       if( field.nodeType == Node.ELEMENT_NODE){
     
              td.appendChild(field);

           }else{
              if(typeof field ==="string"){
                let isimg= field.match(/\.(jpeg|jpg|png|gif|bmp)/i);
                if(isimg){
                    //automatic convert to image
                    let img=createImage(field);
                    td.appendChild(img);
                    
                }else
                   td.appendChild(document.createTextNode(field));
              }
              
           }
       
       
       tr.appendChild(td);
   });
   
   if(click===null){
   }else tr.addEventListener("click",click);
   
   tr.className=myclass;
   return tr;
 }
  //creatre a TR via array
 function crTable(cfields,click=null,myclass="default-class"){
    let table=document.createElement('table');
    cfields.forEach((fields)=>{
       let tr= crTR(fields,click,myclass);
       table.appendChild(tr);
    }
    );
    table.className=myclass;
    return table;
 }
 
 function insertRow(index=0,table,fields,myclass="default-class")
 {
    //index=0 Insert new row(s) at the first position of a table
    row=table.insertRow(index);
    let i=0;
    fields.forEach((field)=>{
       cell=row.insertCell(i);
       //cell.appendChild(document.createTextNode(field));
       if( field.nodeType == Node.ELEMENT_NODE){

       

              cell.appendChild(field);

           }else{
            
            if(typeof field ==="string"){
                let isimg= field.match(/\.(jpeg|jpg|png|gif|bmp)/i);
                if(isimg){
                    //automatic convert to image
                    let img=createImage(field);
                    cell.appendChild(img);
                    
                }else
                   cell.appendChild(document.createTextNode(field));
              }
              
           }
       
       i++;
       
    });
    
 }
 
 //creatre a TH via array
 function crTH(fields,click=null,myclass="default-class"){
   let td;
   
   let tr = document.createElement('th');
   fields.forEach((field)=>{
       td=document.createElement('td');
       if( field.nodeType == Node.ELEMENT_NODE){

       

              td.appendChild(field);

           }else{
              td.appendChild(document.createTextNode(field))
           }
       
       
       tr.appendChild(td);
   });
   
   if(click===null){
   }else tr.addEventListener("click",click);
   
   tr.className=myclass;
   return tr;
 }
 
 function getCells(elem,callback=null)
 {
  var result=[];
  for (let i = 0; i < elem.length; i++) {
    
    if(callback===null){
   }else callback(elem[i]);
   
    result.push(elem[i].innerHTML);
  }
  console.log(result);
  
  return result;
 }
  function genhref(href, linkname, click=null, myclass="default-class"){
   var a = document.createElement('a'); 
                // Create the text node for anchor element.
                var link = document.createTextNode(linkname);
                // Append the text node to anchor element.
                a.appendChild(link); 
                // Set the title.
                a.title = linkname;
                
                a.href =href;
                if(click!=null)a.addEventListener("click",click);
                a.className=myclass;
                console.warn(a);
                //a.appendChild(link);
                
                return a;
 }
 
 function crElems(fields,etype='div',click=null,myclass="default-class"){
     var e=document.createElement(etype);
    // let aa=[];
    // aa=fields;
    //  console.warn("fields:"+fields);
     
      // console.error(fields);
     if( Array.isArray(fields)) {
        
        fields.forEach((field)=>{
      
          if( field.nodeType == Node.ELEMENT_NODE){
              e.appendChild(field);
          }
      
         });
      }else if( fields.nodeType == Node.ELEMENT_NODE){
              //console.warn("element:"+fields);
              e.appendChild(fields);
          }else {
              //other ?
              console.warn("others:"+fields);
          }
       
     
     e.className=myclass;
     return e;
  }   
   function crDivs(cfields,click=null,myclass="default-class"){
    let div=document.createElement('div');
  
    cfields.forEach((fields)=>{
       //console.warn("crDivs:"+fields);
       let el= crElems(fields,'div',click,myclass);
       div.appendChild(el);
       
    }
    );
    div.className=myclass;
    //console.warn("final:"+div);
    return div;
 }
 
 function deleteID(id){
  var e=null;
  var child;
 if( id.nodeType == Node.ELEMENT_NODE)child=id;
 else 
   child=document.getElementById(id);

 
 try{
    e=child.parentNode.removeChild(child);
    
    }catch(e){
     console.warn(e);
 }
 return e;
 
 }
 function appendElement(id,e){
  if( id.nodeType == Node.ELEMENT_NODE){
     return  id.appendChild(e);
  }else  return document.getElementById(id).appendChild(e);
   
 }
 
function  crInput(type,name,value='',myclass="default-class",elem='input'){
//crInput('TEXT','NAME',null,'CNAME');
var input = document.createElement(elem);

input.setAttribute("type", type);
if(type.toLowerCase()=='image'){
 
  input.src=value;

}else{
  input.value=value;
}
//if(click!=null)input.click=click;
input.className = myclass; // set the CSS class
input.name=name;

return input;

}
 
 


使用方法例如:

 <!DOCTYPE html>

<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  
  <script src="js/jelems-1.0.js"></script>
  <style>
    body {
       background-color: lightblue;
    }
   .MYTABLE {
     background-color :#99ccff;
     text-align :center;
     width:100%;
     color:green;
     border: 1px solid black;
     text-align: left;
   }
   .MYTABLE2 {
     background-color :#99ccff;
     text-align :center;
     width:100%;
     color:green;
     border: 1px solid black;
     text-align: center;
   }
   
   



 </style>
  </head>
<body>
<div id='showit'></div><div id='showit2'></div>
<script>
 
 
let tableitems=[['row0','img/apple.jpg'],['row1','cell1'],['row2','cell1']];

let table=crTable(tableitems,null,'MYTABLE');

document.getElementById('showit').appendChild(table);

let tableitems2=[['img/apple.jpg'],['caption- apple'],['img/banana.jpg'],['Fig 2. banana'],['row2','cell1']];
 let table2=crTable(tableitems2,null,'MYTABLE2');

document.getElementById('showit2').appendChild(table2); 
 
</script>


</body>
</html>


ctable 1.1.js

/* Licence:GPL Author: WhoAmI Date: 2021/01/01 Version:1.1 e-mail: Notice that setup cid( class ID) and table ID See also HTMLTableElement.rows */ //cTable 20220701 via WhoAmI function display(id,elem,callback=null){ var v; if(callback!==null)v=callback(elem); else v=elem; if(typeof elem ==='string')document.getElementById(id).innerText=v; if(typeof elem ==='object') return document.getElementById(id).appendChild(v); return v; } class cTable { parentid; tobj; border=''; //'1px solid black'; title; rowclick=undefined; //callback //Notice: class id!!!! elementcid; //element ID elementid; firstrow; constructor(cid,tid='GEN_TABLEID'){ this.elementcid=cid; this.elementid=tid; //default parrent's id this.parentid=document.body; } //return parent show(id=this.parentid){ if(id==='undefined'){ var body= document.body; }else { var body=document.getElementById(id); } body.appendChild(this.tobj); return body; } //create table element create(objectArray, fieldTitles,trclass='TR_NAME') { this.title=fieldTitles; var _tobj = document.createElement('table'); _tobj.classList.add(this.elementcid); let thead = document.createElement('thead'); let thr = document.createElement('tr'); this.firstrow=thr; thr.className=trclass; if(this.rowclick!==undefined)thr.onclick=this.rowclick; fieldTitles.forEach((fieldTitle) => { //console.error(fieldTitle); let th = document.createElement('th'); th.className=trclass; th.appendChild(document.createTextNode(fieldTitle)); thr.appendChild(th); }); thead.appendChild(thr); _tobj.appendChild(thead); let tbdy = document.createElement('tbody'); let tr = document.createElement('tr'); objectArray.forEach((object) => { // console.error(object); let tr = document.createElement('tr'); tr.className=trclass; if(this.rowclick!==undefined)tr.onclick=this.rowclick; let i =0; this.title.forEach((field) => { var td = document.createElement('td'); if( object[i].nodeType === Node.ELEMENT_NODE){ td.appendChild((object[i])); }else{ //find jpg image // if(object[i].match(/.jpg/g)){ if(object[i].match(/[\/.](gif|jpg|jpeg|tiff|png)$/i)){ let img=this.createImage(object[i]); td.appendChild(img); }else{ td.appendChild(document.createTextNode(object[i])); } } tr.appendChild(td); i++; }); tbdy.appendChild(tr); }); _tobj.appendChild(tbdy); this.tobj=_tobj; return _tobj; } //delete one record delete(index=0){ var tbdy=this.tobj; tbdy.deleteRow(index); return tbdy.rows.length; } //select element createSelect(name,cname,array){ var selectList = document.createElement("select"); selectList.className = cname; selectList.name=name; //Create and append the options for (var i = 0; i < array.length; i++) { var option = document.createElement("option"); option.value = array[i]; option.text = array[i]; selectList.appendChild(option); } return selectList; } //img createImage(src,cname='IMG_CLASS'){ var img = document.createElement('img'); img.src = src; img.className=cname; return img; } createInput(type,name,cname,elem='input',value=''){ //createInput('TEXT','NAME','CNAME'); var input = document.createElement(elem); input.setAttribute("type", type); if(type.toLowerCase()=='image'){ input.src=value; }else{ input.value=value; } input.className = cname; // set the CSS class input.name=name; return input; } //insert one record insert(objectArray,trclass='TR_NAME' ){ //let body = document.getElementsByTagName('body')[0]; var tbdy=this.tobj; objectArray.forEach((object) => { let tr = document.createElement('tr'); if(this.rowclick!=='undefined')tr.onclick=this.rowclick; tr.className=trclass; let i =0; this.title.forEach((field) => { //let i =0; var td = document.createElement('td'); //console.error(field+object[i]); //check nodType //console.error(object[i].nodeType); if( object[i].nodeType === undefined){ //FIXIT!!!! // if( object[i]!== undefined){ //Node.ELEMENT_NODE //console.log(object[i].innerHTML); //td.appendChild((object[i])); console.log("undefined:"+object[i]); if(typeof object[i] === "string"){ if(object[i].match(/[\/.](gif|jpg|jpeg|tiff|png)$/i)){ let img=this.createImage(object[i]); td.appendChild(img); }else{ td.appendChild(document.createTextNode(object[i])); } }else if( object[i].nodeType === Node.ELEMENT_NODE){ td.appendChild((object[i])); } }else{ // JS RegExp if(typeof object[i] === "string") { console.log(object[i]); if(object[i].match(/[\/.](gif|jpg|jpeg|tiff|png)$/i)){ let img=this.createImage(object[i]); td.appendChild(img); }else{ td.appendChild(document.createTextNode(object[i])); } }else if( object[i].nodeType === Node.ELEMENT_NODE){ td.appendChild((object[i])); } } tr.appendChild(td); i++; }); /* *You can try firstChild, lastChild * */ tbdy.appendChild(tr); }); return this.tobj.rows.length; } /* * *FIXME!!! */ insertBefore(index, objectArray,trclass='TR_NAME' ){ //let body = document.getElementsByTagName('body')[0]; var tbdy=this.tobj; objectArray.forEach((object) => { let tr = document.createElement('tr'); if(this.rowclick!=='undefined')tr.onclick=this.rowclick; tr.className=trclass; let i =0; this.title.forEach((field) => { //let i =0; var td = document.createElement('td'); //console.error(field+object[i]); //check nodType //console.error(object[i].nodeType); if( object[i].nodeType == undefined){ //FIXIT!!!! // if( object[i]!== undefined){ //Node.ELEMENT_NODE //console.log(object[i].innerHTML); //td.appendChild((object[i])); td.appendChild(document.createTextNode(object[i])); }else{ // JS RegExp if(object[i].match(/[\/.](gif|jpg|jpeg|tiff|png)$/i)){ let img=this.createImage(object[i]); td.appendChild(img); }else{ td.appendChild(document.createTextNode(object[i])); } } tr.appendChild(td); i++; }); /* *You can try firstChild, lastChild * *myrows = mytable.rows; *firstRow = mytable.rows[0]; *lastRow = mytable.rows.item(mytable.rows.length-1); *parentNode */ let firstRow=tbdy.rows[index]; firstRow.parentNode.insertBefore(tr,firstRow); //tbdy.insertBefore(tr,firstRow); //tbdy.appendChild(tr); }); return this.tobj.rows.length; } } //end of class
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <script src="ctable-1.1.js"></script> <style> body { background-color: lightblue; } .IMG_CLASS{ width: 50%; } .demodiv { border-style: solid; width:80%; } .TR_NAME { background-color: green; border-style: solid; } </style> </head> <body> <h2>ctable.js TEST</h2> <div id="elem"></div> <div id="demodiv"></div> <script> //demo class cTable tobj=new cTable('demodiv'); //tobj.border='1px solid black'; //Create INPUT TEborder-style: solid;XT var input=tobj.createInput('TEXT','tName','CNAME'); //setup onclick on input if you need it //input.onclick=add; //Create select var array=['car','fruit','dog']; //option array var sel=tobj.createSelect('sName','CNAME',array); document.getElementById('elem').appendChild(sel); //Create hidden var hidden=tobj.createInput('HIDDEN','sName','CNAME'); //setup onclick //hidden.onclick=add; //create button var btn=tobj.createInput('button','bName','BNAME','input','Submit'); //document.body.appendChild(input); //create checkbox var chk=tobj.createInput('checkbox','cName','CNAME'); //create image input var img=tobj.createInput('image','iName','CNAME','input','img/enter.png'); //create image var image=tobj.createImage('img/banana.jpg'); var ftitle=['--zero--','--one--','--two--','--three--']; var ffield=['ZERO',image,'TWO','THREE']; var ffield2=[image,chk,'img/dancing.gif','THREE']; //var iifield=[input,btn,image,sel]; tabobj=tobj.create([ffield2],ftitle); tobj.show('demodiv'); var iifield=[input,btn,'img/apple.jpg',sel]; //insert two arrays console.log(iifield); let index=tobj.insert([iifield,ffield2]); //insert textnode index=tobj.insert([ffield]); </script> </body> </html>