Java HTTP server sending chunked response -


i working on java application has built in http server, @ moment server implemented using serversocketchannel, listens on port 1694 requests:

        msvrch = serversocketchannel.open();         msvrch.socket().bind(new inetsocketaddress(mintport));         msvrch.configureblocking(false); 

a thread installed manage requests , responses:

        thread thrd = new thread(msgreceiver);         thrd.setuncaughtexceptionhandler(exceptionhandler);         thrd.start(); 

the thread quite simple:

        runnable msgreceiver = new runnable() {             @override             public void run() {                 try{                     while( !thread.interrupted() ) {     //sleep short period between checks new requests                                                   try{                             thread.sleep(delay_between_accepts);                         } catch(exception ex) {                             ex.printstacktrace();                         }                                                    socketchannel clich = msvrch.accept();                          if ( blnexit() == true ) {                             break;                         }                                                    if ( clich == null ) {                             continue;                         }                         processrequest(clich.socket());                     }                                        } catch (ioexception ex) {                     ex.printstacktrace();                 } {                                          logmsg(terminating_thread +                              "for accepting cluster connections", true);                      if ( msvrch != null ) {                         try {                             msvrch.close();                         } catch (ioexception ex) {                             ex.printstacktrace();                         }                         msvrch = null;                     }                 }                            }         }; 

the main bulk of code dealing response in function processrequest:

private void processrequest(socket sck) {     try {     //ajax parameters         final string ajax_id            = "ajmid";     //the 'handler key' used decode response                  final string handler_key        = "hkey";     //message payload                    final string payload            = "payload";     //post input buffer size                     final int request_buffer_size   = 4096;     //double carriage return marks end of headers                    final string crlf               = "\r\n";          bufferedreader in = new bufferedreader(new inputstreamreader(sck.getinputstream()));         string stramid = null, strhkey = null, strrequest;         char[] chrbuffer = new char[request_buffer_size];         stringbuffer sbrequest = new stringbuffer();         emsgtypes etype = emsgtypes.unknown;         clshttpparameters objparams = null;         int intpos, intcount;                    //extract entire request, including headers                  if ( (intcount = in.read(chrbuffer)) == 0 ) {             throw new exception("cannot read request!");         }         sbrequest.append(chrbuffer, 0, intcount);                    strrequest = sbrequest.tostring();     //what method being used request?         if ( strrequest.startswith(http_get) ) {     //the request should end http marker, remove before trying interpret data             if ( strrequest.indexof(http_marker) != -1 ) {                 strrequest = strrequest.substring(0, strrequest.indexof(http_marker)).trim();             }                 //look data marker             if ( (intpos = strrequest.indexof(http_data_start)) >= 0 ) {     //data present in query, skip start of data                 strrequest = strrequest.substring(intpos + 1);             } else {     //remove method indicator                 strrequest = strrequest.substring(http_get.length());                                }         } else if ( strrequest.startswith(http_post) ) {     //discard headers , jump data             if ( (intpos = strrequest.lastindexof(crlf)) >= 0 ) {                 strrequest = strrequest.substring(intpos + crlf.length());               }         }         if ( strrequest.length() > 1 ) {     //extract parameters                                 objparams = new clshttpparameters(strrequest);         }                     if ( strrequest.startswith("/") == true ) {     //look document reference             strrequest = strrequest.substring(1);                            etype = emsgtypes.send_doc;                      }         if ( objparams != null ) {     //transfer payload request             string strpayload = objparams.getvalue(payload);              if ( strpayload != null ) {                 byte[] arybytpayload = base64.decodebase64(strpayload.getbytes());                  strrequest = new string(arybytpayload);                 stramid = objparams.getvalue(ajax_id);                 strhkey = objparams.getvalue(handler_key);             }         }          if ( etype == emsgtypes.unknown            && strrequest.startswith("{") && strrequest.endswith("}") ) {     //the payload json, there type parameter?             string strtype = strgetjsonitem(strrequest, json_lbl_type);              if ( strtype != null && strtype.length() > 0 ) {     //decode type                                    etype = emsgtypes.valueof(strtype.touppercase().trim());     //what system message from?                 string strip = strgetjsonitem(strrequest, json_lbl_ip)                       ,strmac = strgetjsonitem(strrequest, json_lbl_mac);                                    if ( strip != null && strip.length() > 0                  && strmac != null && strmac.length() > 0 ) {     //is system known in cluster?                     clsipmon objsystem = objaddsystocluster(strip, strmac);                      if ( objsystem != null ) {     //update date/time stamp of remote system                                                    objsystem.touch();                                               }     //this internal cluster message, no response required                     return;                 }                                }         }                     string strcontenttype = null, strresppayload = null;         outputstream out = sck.getoutputstream();         byte[] arybytresponse = null;         boolean blnshutdown = false;         out.write("http/1.0 200\n".getbytes());          switch( etype ) {         case send_doc:             if ( strrequest.length() <= 1 ) {                 strrequest = html_root + default_doc;             } else {                 strrequest = html_root + strrequest;             }             logmsg("http request for: " + strrequest, true);              if ( strrequest.tolowercase().endswith(".css") == true ) {                 strcontenttype = mime_css;             } else if ( strrequest.tolowercase().endswith(".gif") == true ) {                 strcontenttype = mime_gif;             } else if ( strrequest.tolowercase().endswith(".jpg") == true ) {                 strcontenttype = mime_jpg;             } else if ( strrequest.tolowercase().endswith(".js") == true ) {                 strcontenttype = mime_js;             } else if ( strrequest.tolowercase().endswith(".png") == true ) {                 strcontenttype = mime_png;             } else if ( strrequest.tolowercase().endswith(".html") == true                       || strrequest.tolowercase().endswith(".htm") == true ) {                 strcontenttype = mime_html;             }             file objfile = new file(strrequest);              if ( objfile.exists() == true ) {                 fileinputstream objfis = new fileinputstream(objfile);                  if ( objfis != null ) {                     arybytresponse = new byte[(int)objfile.length()];                      if ( objfis.read(arybytresponse) == 0 ) {                         arybytresponse = null;                     }                     objfis.close();                 }             }             break;         case channel_sts:             strresppayload = strchannelstatus(strrequest);             strcontenttype = mime_json;             break;         case cluster_sts:             strresppayload = strclusterstatus();             strcontenttype = mime_json;              break;         case module_sts:             strresppayload = strmodulestatus(strrequest);             strcontenttype = mime_json;             break;         case network_inf:             strresppayload = strnetworkinfo(strrequest);             strcontenttype = mime_json;             break;         case node_sts:             strresppayload = strnodestatus(strrequest);             strcontenttype = mime_json;             break;         case poll_sts:             strresppayload = strpollstatus(strrequest);             strcontenttype = mime_json;             break;         case sys_sts:     //issue system status                            strresppayload = strappstatus();             strcontenttype = mime_json;             break;                   case shutdown:     //issue instruction restart system             strresppayload = "shutdown in progress!";             strcontenttype = mime_plain;     //flag shutdown has been requested                          blnshutdown = true;             break;         default:         }         if ( strresppayload != null ) {     //convert response string byte array                          arybytresponse = strresppayload.getbytes();     system.out.println("[ " + strresppayload.length() + " ]: " + strresppayload);           //hack                   }                    if ( arybytresponse != null && arybytresponse.length > 0 ) {             if ( strcontenttype == mime_json ) {                 string strresponse = "{";                  if ( stramid != null ) {     //include request ajax message id in response                     if ( strresponse.length() > 1 ) {                         strresponse += ",";                     }                        strresponse += "\"" + ajax_id + "\":" + stramid;                 }                 if ( strhkey != null ) {                     if ( strresponse.length() > 1 ) {                         strresponse += ",";                     }                     strresponse += "\"" + handler_key + "\":\"" + strhkey + "\"";                 }                 if ( strresponse.length() > 1 ) {                     strresponse += ",";                 }                 strresponse += "\"payload\":" + new string(arybytresponse)                               + "}";                                  arybytresponse = strresponse.getbytes();             }             string strheaders = "";              if ( strcontenttype != null ) {                 strheaders += "content-type: " + strcontenttype + "\n";                              }             strheaders += "content-length: " + arybytresponse.length + "\n"                          + "access-control-allow-origin: *\n"                         + "access-control-allow-methods: post, get, options, delete, put\n"                         + "access-control-allow-credentials: true\n"                         + "keep-alive: timeout=2, max=100\n"                         + "cache-control: no-cache\n"                          + "pragma: no-cache\n\n";             out.write(strheaders.getbytes());             out.write(arybytresponse);             out.flush();                         }         out.close();         sck.close();          if ( blnshutdown == true ) {             string strsystem =  mobjlocalip.strgetip();              if ( strsystem.compareto(mobjlocalip.strgetip()) != 0 ) {     //specified system not local system, issue message remote system.                 broadcastmessage("{\"" + json_lbl_type  + "\":\"" +                                                     emsgtypes.shutdown + "\""                                + ",\"" + json_lbl_time  + "\":\"" +                                             clstimeman.lngtimenow() + "\"}");                                         } else {     //shutdown addressed local system                                     if ( getos().indexof("linux") >= 0 ) {     //to do!!!                                   } else if ( getos().indexof("win") >= 0 ) {                     runtime runtime = runtime.getruntime();                     runtime.exec("shutdown /r /c \"shutdown request\" /t 0 /f");                     system.exit(exitcode_requested_shutdown);                 }                            }         }     } catch (exception ex) {                 } {         if (sck != null) {             try {                 sck.close();             } catch (ioexception ex) {                 ex.printstacktrace();             }         }     } } 

i implemented chunked response, @ present chunked responses not supported code above.

[edit] i've tried implement chunked response adding method:

    /**      * @param strdata - data split chunks      * @return string array containing chunks      */  public static string[] arystrchunkdata(string strdata) {     int intchunks = (strdata.length() / chunk_threshold_bytesize) + 1;     string[] arystrchunks = new string[intchunks];     int intlength = strdata.length(), intpos = 0;      for( int c=0; c<arystrchunks.length; c++ ) {                     if ( intpos < intlength ) {     //extract chunk data                      int intend = math.min(intlength - 1, intpos + chunk_threshold_bytesize);             arystrchunks[c] = strdata.substring(intpos, intend);         }     //advance data position next chunk                    intpos += chunk_threshold_bytesize;     }            return arystrchunks; } 

the modified processrequest looks this:

        private void processrequest(socket sck) {     try {         //ajax parameters         final string ajax_id            = "ajmid";         //the 'handler key' used decode response                  final string handler_key        = "hkey";         //message payload                    final string payload            = "payload";         //post input buffer size                     final int request_buffer_size   = 4096;         //double carriage return marks end of headers                    final string crlf               = "\r\n";          bufferedreader in = new bufferedreader(new inputstreamreader(sck.getinputstream()));         string stramid = null, strhkey = null, strrequest;         char[] chrbuffer = new char[request_buffer_size];         stringbuffer sbrequest = new stringbuffer();         emsgtypes etype = emsgtypes.unknown;         clshttpparameters objparams = null;         int intpos, intcount;                        //extract entire request, including headers                  if ( (intcount = in.read(chrbuffer)) == 0 ) {             throw new exception("cannot read request!");         }         sbrequest.append(chrbuffer, 0, intcount);                    strrequest = sbrequest.tostring();         //what method being used request?         if ( strrequest.startswith(http_get) ) {         //the request should end http marker, remove before trying interpret data             if ( strrequest.indexof(http_marker) != -1 ) {                 strrequest = strrequest.substring(0, strrequest.indexof(http_marker)).trim();             }                     //look data marker             if ( (intpos = strrequest.indexof(http_data_start)) >= 0 ) {         //data present in query, skip start of data                 strrequest = strrequest.substring(intpos + 1);             } else {         //remove method indicator                 strrequest = strrequest.substring(http_get.length());                                }         } else if ( strrequest.startswith(http_post) ) {         //discard headers , jump data             if ( (intpos = strrequest.lastindexof(crlf)) >= 0 ) {                 strrequest = strrequest.substring(intpos + crlf.length());               }         }         if ( strrequest.length() > 1 ) {         //extract parameters                                 objparams = new clshttpparameters(strrequest);         }                     if ( strrequest.startswith("/") == true ) {         //look document reference             strrequest = strrequest.substring(1);                            etype = emsgtypes.send_doc;                      }         if ( objparams != null ) {         //transfer payload request             string strpayload = objparams.getvalue(payload);              if ( strpayload != null ) {                 byte[] arybytpayload = base64.decodebase64(strpayload.getbytes());                  strrequest = new string(arybytpayload);                 stramid = objparams.getvalue(ajax_id);                 strhkey = objparams.getvalue(handler_key);             }         }          if ( etype == emsgtypes.unknown            && strrequest.startswith("{") && strrequest.endswith("}") ) {         //the payload json, there type parameter?             string strtype = strgetjsonitem(strrequest, json_lbl_type);                if ( strtype != null && strtype.length() > 0 ) {         //decode type                                    etype = emsgtypes.valueof(strtype.touppercase().trim());         //what system message from?                 string strip = strgetjsonitem(strrequest, json_lbl_ip)                       ,strmac = strgetjsonitem(strrequest, json_lbl_mac);                                    if ( strip != null && strip.length() > 0                  && strmac != null && strmac.length() > 0 ) {         //is system known in cluster?                     clsipmon objsystem = objaddsystocluster(strip, strmac);                      if ( objsystem != null ) {         //update date/time stamp of remote system                                                    objsystem.touch();                                               }         //this internal cluster message, no response required                     return;                 }                                }         }                     string strcontenttype = null, strresppayload = null;                     outputstream out = sck.getoutputstream();         byte[] arybytresponse = null;         boolean blnshutdown = false;         //start writing headers         string strheaders = "http/1.0 200\n"                           + "date: " + (new date()).tostring() + "\n"                           + "access-control-allow-origin: *\n"                           + "access-control-allow-methods: post, get, options, delete, put\n"                           + "access-control-allow-credentials: true\n"                           + "keep-alive: timeout=2, max=100\n"                           + "cache-control: no-cache\n"                            + "pragma: no-cache\n";                     out.write(strheaders.getbytes());         strheaders = "";          switch( etype ) {         case send_doc:             if ( strrequest.length() <= 1 ) {                 strrequest = html_root + default_doc;             } else {                 strrequest = html_root + strrequest;             }             logmsg("http request for: " + strrequest, true);              if ( strrequest.tolowercase().endswith(".css") == true ) {                 strcontenttype = mime_css;             } else if ( strrequest.tolowercase().endswith(".gif") == true ) {                 strcontenttype = mime_gif;             } else if ( strrequest.tolowercase().endswith(".jpg") == true ) {                 strcontenttype = mime_jpg;             } else if ( strrequest.tolowercase().endswith(".js") == true ) {                 strcontenttype = mime_js;             } else if ( strrequest.tolowercase().endswith(".png") == true ) {                 strcontenttype = mime_png;             } else if ( strrequest.tolowercase().endswith(".html") == true                       || strrequest.tolowercase().endswith(".htm") == true ) {                 strcontenttype = mime_html;             }             file objfile = new file(strrequest);              if ( objfile.exists() == true ) {                 fileinputstream objfis = new fileinputstream(objfile);                  if ( objfis != null ) {                     arybytresponse = new byte[(int)objfile.length()];                      if ( objfis.read(arybytresponse) == 0 ) {                         arybytresponse = null;                     }                     objfis.close();                 }             }             break;         case channel_sts:             strresppayload = strchannelstatus(strrequest);             strcontenttype = mime_json;             break;         case cluster_sts:             strresppayload = strclusterstatus();             strcontenttype = mime_json;              break;         case module_sts:             strresppayload = strmodulestatus(strrequest);             strcontenttype = mime_json;             break;         case network_inf:             strresppayload = strnetworkinfo(strrequest);             strcontenttype = mime_json;             break;         case node_sts:             strresppayload = strnodestatus(strrequest);             strcontenttype = mime_json;             break;         case poll_sts:             strresppayload = strpollstatus(strrequest);             strcontenttype = mime_json;             break;         case sys_sts:         //issue system status                            strresppayload = strappstatus();             strcontenttype = mime_json;             break;                   case shutdown:         //issue instruction restart system             strresppayload = "shutdown in progress!";             strcontenttype = mime_plain;         //flag shutdown has been requested                          blnshutdown = true;             break;         default:         }         if ( strresppayload != null ) {         //convert response string byte array                          arybytresponse = strresppayload.getbytes();         }                    if ( arybytresponse != null && arybytresponse.length > 0 ) {             boolean blnchunked = false;              if ( strcontenttype != null ) {                 strheaders += "content-type: " + strcontenttype + "\n";                              }                            if ( strcontenttype == mime_json ) {                 string strresponse = "{";                  if ( stramid != null ) {         //include request ajax message id in response                     if ( strresponse.length() > 1 ) {                         strresponse += ",";                     }                        strresponse += "\"" + ajax_id + "\":" + stramid;                 }                 if ( strhkey != null ) {                     if ( strresponse.length() > 1 ) {                         strresponse += ",";                     }                     strresponse += "\"" + handler_key + "\":\"" + strhkey + "\"";                 }                 if ( strresponse.length() > 1 ) {                     strresponse += ",";                 }                 strresponse += "\"payload\":" + new string(arybytresponse)                               + "}";         //how big response?     if ( strresponse.length() > chunk_threshold_bytesize ) {                     blnchunked = true;                     strheaders += "transfer-encoding: chunked\n\n";                     out.write(strheaders.getbytes());         //slice string chunks                             string[] arystrchunks = arystrchunkdata(strresponse);                      for( int c=0; c<arystrchunks.length; c++ ) {                         string strchunk = arystrchunks[c];                          if ( strchunk != null ) {                             string strlength = integer.tohexstring(strchunk.length()) + "\r\n";                             strchunk += "\r\n";                             out.write(strlength.getbytes());                             out.write(strchunk.getbytes());                         }                                                }         //last chunk 0 bytes                                           out.write("0\r\n\r\n".getbytes());                 } else {                     arybytresponse = strresponse.getbytes();                 }             }             if ( blnchunked == false ) {                     strheaders += "content-length: " + arybytresponse.length + "\n\n";                                           out.write(strheaders.getbytes());                 out.write(arybytresponse);             }             out.flush();                         }         out.close();         sck.close();          if ( blnshutdown == true ) {             string strsystem =  mobjlocalip.strgetip();              if ( strsystem.compareto(mobjlocalip.strgetip()) != 0 ) {         //specified system not local system, issue message remote system.                 broadcastmessage("{\"" + json_lbl_type  + "\":\"" +                                                     emsgtypes.shutdown + "\""                                + ",\"" + json_lbl_time  + "\":\"" +                                             clstimeman.lngtimenow() + "\"}");                                         } else {     //shutdown addressed local system                                     if ( getos().indexof("linux") >= 0 ) {         //to do!!!                                   } else if ( getos().indexof("win") >= 0 ) {                     runtime runtime = runtime.getruntime();                     runtime.exec("shutdown /r /c \"shutdown request\" /t 0 /f");                     system.exit(exitcode_requested_shutdown);                 }                            }         }     } catch (exception ex) {                 } {         if (sck != null) {             try {                 sck.close();             } catch (ioexception ex) {                 ex.printstacktrace();             }         }     } } 

i've read several specifications chunked responses , far can tell sending data in correct format, don't receive in browser.

i may have wrongly assume browser correctly piece chunks one, wrong. client side handler looks this:

      this.responsehandler = function() { try {       if ( mobjhttp == null   || !(mobjhttp.readystate == 4 && mobjhttp.status == 200)   || !(mstrresponsetext = mobjhttp.responsetext)   || mstrresponsetext.length == 0 ) {     //not ready or no response decode           return;   }     //do response     } catch( ex ) {   t.error("responsehandler:", ex); } 

};

this handler set-up elsewhere in object:

    mobjhttp.onreadystatechange = this.responsehandler; 

solved, not sure why, removing header:

  transfer-encoding: chunked 

and chunk lengths @ beginning of each chunk resolved issue, still write data in 768 byte chunks. works reliably , well.

not sure why had this.

final method produce chunks data string:

    public static string[] arystrchunkdata(string strdata) {             int intchunks = (strdata.length() / chunk_threshold_bytesize) + 1;             string[] arystrchunks = new string[intchunks];             int intlength = strdata.length(), intpos = 0;              for( int c=0; c<arystrchunks.length; c++ ) {                             if ( intpos < intlength ) {     //extract chunk data                              int intend = math.min(intlength, intpos + chunk_threshold_bytesize);                     arystrchunks[c] = strdata.substring(intpos, intend);                     intpos = intend;                 }             }                    return arystrchunks;         } 

loop write chunks, no lengths @ beginning , no 0 byte @ end of chunks required:

    string[] arystrchunks = arystrchunkdata(strresponse);     for( string strchunk : arystrchunks ) {             if ( strchunk != null ) {                     out.write(strchunk.getbytes());             }                                } 

Comments

Popular posts from this blog

javascript - Chart.js (Radar Chart) different scaleLineColor for each scaleLine -

apache - Error with PHP mail(): Multiple or malformed newlines found in additional_header -

android - Go back to previous fragment -