1919import java .io .PrintWriter ;
2020import java .lang .reflect .Constructor ;
2121import java .lang .reflect .InvocationTargetException ;
22+ import java .net .URLDecoder ;
2223import java .util .Date ;
2324import java .util .Enumeration ;
2425import java .util .HashSet ;
@@ -273,104 +274,39 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
273274 protected void doGet (HttpServletRequest req , HttpServletResponse resp )
274275 throws ServletException , IOException {
275276 String request = req .getQueryString ();
277+ // a quick check!
278+ if (request == null || request .length () < 4 ) {
279+ resp .sendError (HttpServletResponse .SC_BAD_REQUEST );
280+ return ;
281+ }
282+
283+ boolean isScriptReuest = false ;
284+ String requestID = null ;
276285 /*
277286 * may be start with "jz[n|p|c|z]=", normal request may start with
278- * "WLL100" or other tokens but charAt(3) should never be '='.
287+ * raw "WLL100" or other tokens but charAt(3) should never be '='.
279288 */
280- boolean isScriptRequest = request .charAt (3 ) == '=' ;
281-
282- boolean supportScriptRequest = supportXSSRequest ();
283-
284- String nameID = null ;
285- if (supportScriptRequest ) {
286- HttpSession ses = req .getSession (false );
287- if (ses != null ) { // try to clean expired request!
288- Enumeration attrNames = ses .getAttributeNames ();
289- while (attrNames .hasMoreElements ()) {
290- String name = (String ) attrNames .nextElement ();
291- if (name .startsWith ("jzt" )) {
292- Date dt = (Date ) ses .getAttribute (name );
293- if (new Date ().getTime () - dt .getTime () > maxXSSRequestLatency ()) {
294- ses .removeAttribute (name );
295- ses .removeAttribute ("jzn" + name .substring (3 ));
296- }
297- }
298- }
289+ if (request .charAt (3 ) == '=' ) { // simplerpc?jzn=604107&jzp=1&jzc=1&jzz=WLL100...
290+ request = req .getParameter ("jzz" ); // jzz without jzn is considered as XHR requests!
291+ if (request == null || request .trim ().length () == 0 ) {
292+ resp .sendError (HttpServletResponse .SC_BAD_REQUEST );
293+ return ;
299294 }
300- if (isScriptRequest ) { // simplerpc?jzn=604107&jzp=1&jzc=1&jzz=WLL100...
301- nameID = req .getParameter ("jzn" );
302- String count = req .getParameter ("jzp" );
303- String current = req .getParameter ("jzc" );
304- String content = req .getParameter ("jzz" );
305- if (nameID == null || !nameID .matches ("\\ d{6,}" )
306- || count == null || !count .matches ("[1-9]\\ d{0,2}" )
307- || current == null || !current .matches ("[1-9]\\ d{0,2}" )
308- || content == null || content .trim ().length () == 0 ) {
309- resp .sendError (HttpServletResponse .SC_BAD_REQUEST );
295+
296+ requestID = req .getParameter ("jzn" );
297+ if (requestID != null && requestID .length () != 0 ) {
298+ isScriptReuest = true ;
299+
300+ // when jzn is defined, it's considered as a script request!
301+ request = prepareScriptRequest (req , resp , requestID , request );
302+ if (request == null ) { // already send out reponses
310303 return ;
311304 }
312- int partsCount = Integer .parseInt (count );
313- int curPart = Integer .parseInt (current );
314- if (curPart > partsCount ) {
315- resp .sendError (HttpServletResponse .SC_BAD_REQUEST );
316- return ;
317- }
318- if (partsCount > maxXSSRequestParts ()) {
319- resp .setContentType ("text/javascript" );
320- //resp.setCharacterEncoding("utf-8");
321- resp .getWriter ().write ("net.sf.j2s.ajax.SimpleRPCRequest" +
322- ".xssNotify(\" " + nameID + "\" , \" exceedrequestlimit\" );" );
323- return ;
324- }
325- if (partsCount != 1 ) {
326- HttpSession session = req .getSession ();
327- String attrName = "jzn" + nameID ;
328- String attrTime = "jzt" + nameID ;
329- Object attr = session .getAttribute (attrName );
330- String [] parts = null ;
331- if (attr == null ) {
332- parts = new String [partsCount ];
333- session .setAttribute (attrName , parts );
334- session .setAttribute (attrTime , new Date ());
335- } else { // attr instanceof String[]
336- parts = (String []) attr ;
337- if (partsCount != parts .length ) {
338- resp .sendError (HttpServletResponse .SC_BAD_REQUEST );
339- return ;
340- }
341- }
342- parts [curPart - 1 ] = content ;
343- for (int i = 0 ; i < parts .length ; i ++) {
344- if (parts [i ] == null ) {
345- resp .setContentType ("text/javascript" );
346- //resp.setCharacterEncoding("utf-8");
347- resp .getWriter ().write ("net.sf.j2s.ajax.SimpleRPCRequest" +
348- ".xssNotify(\" " + nameID + "\" , \" continue\" );" );
349- return ;
350- }
351- }
352- synchronized (session ) {
353- session .removeAttribute (attrName );
354- session .removeAttribute (attrTime );
355- }
356- StringBuffer buf = new StringBuffer ();
357- for (int i = 0 ; i < parts .length ; i ++) {
358- buf .append (parts [i ]);
359- parts [i ] = null ;
360- }
361- request = buf .toString ();
362- } else { // bad request !
363- request = content ;
364- }
365305 }
366- } else if (isScriptRequest ) {
367- resp .setContentType ("text/javascript" );
368- //resp.setCharacterEncoding("utf-8");
369- nameID = req .getParameter ("jzn" );
370- resp .getWriter ().write ("net.sf.j2s.ajax.SimpleRPCRequest" +
371- ".xssNotify(\" " + nameID + "\" , \" unsupported\" );" );
372- return ;
306+ } else { // ?WLL100net.sf....%23...
307+ request = URLDecoder .decode (request , "UTF-8" );
373308 }
309+
374310 SimpleRPCRunnable runnable = getRunnableByRequest (request );
375311 if (runnable == null ) {
376312 resp .sendError (HttpServletResponse .SC_NOT_FOUND );
@@ -379,23 +315,138 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
379315 runnable .deserialize (request );
380316 runnable .ajaxRun ();
381317 String serialize = runnable .serialize ();
382- if (!isScriptRequest ) {
383- resp .setContentType ("text/plain" );
384- //resp.setCharacterEncoding("utf-8");
385- PrintWriter writer = resp .getWriter ();
386- writer .write (serialize );
387- } else {
318+
319+ if (isScriptReuest ) { // cross site script response
388320 resp .setContentType ("text/javascript" );
389321 //resp.setCharacterEncoding("utf-8");
390322 PrintWriter writer = resp .getWriter ();
391323 writer .write ("net.sf.j2s.ajax.SimpleRPCRequest.xssNotify(" );
392- writer .write ("\" " + nameID + "\" , \" " );
324+ writer .write ("\" " + requestID + "\" , \" " );
393325 writer .write (serialize .replaceAll ("\r " , "\\ r" )
394326 .replaceAll ("\n " , "\\ n" )
395327 .replaceAll ("\" " , "\\ \" " ));
396328 writer .write ("\" );" );
329+ return ;
330+ }
331+
332+ // normal text response
333+ resp .setContentType ("text/plain" );
334+ //resp.setCharacterEncoding("utf-8");
335+ PrintWriter writer = resp .getWriter ();
336+ writer .write (serialize );
337+ }
338+
339+ private String prepareScriptRequest (HttpServletRequest req , HttpServletResponse resp ,
340+ String scriptRequestID , String request ) throws IOException {
341+
342+ // check request id: must be 6 digitals
343+ if (!scriptRequestID .matches ("\\ d{6,}" )) {
344+ resp .sendError (HttpServletResponse .SC_BAD_REQUEST );
345+ return null ;
346+ }
347+
348+ // make sure that servlet support cross site script request
349+ if (!supportXSSRequest ()) {
350+ resp .setContentType ("text/javascript" );
351+ //resp.setCharacterEncoding("utf-8");
352+ resp .getWriter ().write ("net.sf.j2s.ajax.SimpleRPCRequest" +
353+ ".xssNotify(\" " + scriptRequestID + "\" , \" unsupported\" );" );
354+ return null ;
355+ }
356+
357+ // check script request counts
358+ String count = req .getParameter ("jzp" );
359+ if (count == null || !count .matches ("[1-9]\\ d{0,2}" )) {
360+ resp .sendError (HttpServletResponse .SC_BAD_REQUEST );
361+ return null ;
362+ }
363+ int partsCount = Integer .parseInt (count );
364+ if (partsCount == 1 ) {
365+ return request ; // can be return directly
366+ }
367+
368+ // check curent request index
369+ String current = req .getParameter ("jzc" );
370+ if (current == null || !current .matches ("[1-9]\\ d{0,2}" )) {
371+ resp .sendError (HttpServletResponse .SC_BAD_REQUEST );
372+ return null ;
373+ }
374+ int curPart = Integer .parseInt (current );
375+ if (partsCount < 1 || curPart > partsCount ) {
376+ resp .sendError (HttpServletResponse .SC_BAD_REQUEST );
377+ return null ;
378+ }
379+
380+ // check whether servlet can deal the requests
381+ if (partsCount > maxXSSRequestParts ()) {
382+ resp .setContentType ("text/javascript" );
383+ //resp.setCharacterEncoding("utf-8");
384+ resp .getWriter ().write ("net.sf.j2s.ajax.SimpleRPCRequest" +
385+ ".xssNotify(\" " + scriptRequestID + "\" , \" exceedrequestlimit\" );" );
386+ return null ;
387+ }
388+
389+ // clean dead session bodies
390+ cleanSession (req );
391+
392+ // store request in session before the request is completed
393+ HttpSession session = req .getSession ();
394+ String attrName = "jzn" + scriptRequestID ;
395+ String attrTime = "jzt" + scriptRequestID ;
396+ Object attr = session .getAttribute (attrName );
397+ String [] parts = null ;
398+ if (attr == null ) {
399+ parts = new String [partsCount ];
400+ session .setAttribute (attrName , parts );
401+ session .setAttribute (attrTime , new Date ());
402+ } else { // attr instanceof String[]
403+ parts = (String []) attr ;
404+ if (partsCount != parts .length ) {
405+ resp .sendError (HttpServletResponse .SC_BAD_REQUEST );
406+ return null ;
407+ }
408+ }
409+ parts [curPart - 1 ] = request ;
410+ for (int i = 0 ; i < parts .length ; i ++) {
411+ if (parts [i ] == null ) {
412+ // not completed yet! just response and wait next request.
413+
414+ resp .setContentType ("text/javascript" );
415+ //resp.setCharacterEncoding("utf-8");
416+ resp .getWriter ().write ("net.sf.j2s.ajax.SimpleRPCRequest" +
417+ ".xssNotify(\" " + scriptRequestID + "\" , \" continue\" );" );
418+ return null ;
419+ }
397420 }
421+
422+ // request is completed. return the request
423+ synchronized (session ) {
424+ session .removeAttribute (attrName );
425+ session .removeAttribute (attrTime );
426+ }
427+ StringBuffer buf = new StringBuffer ();
428+ for (int i = 0 ; i < parts .length ; i ++) {
429+ buf .append (parts [i ]);
430+ parts [i ] = null ;
431+ }
432+ return buf .toString ();
398433 }
399434
435+ private void cleanSession (HttpServletRequest req ) {
436+ HttpSession ses = req .getSession (false );
437+ if (ses != null ) { // try to clean expired request!
438+ Enumeration attrNames = ses .getAttributeNames ();
439+ while (attrNames .hasMoreElements ()) {
440+ String name = (String ) attrNames .nextElement ();
441+ if (name .startsWith ("jzt" )) {
442+ Date dt = (Date ) ses .getAttribute (name );
443+ if (new Date ().getTime () - dt .getTime () > maxXSSRequestLatency ()) {
444+ ses .removeAttribute (name );
445+ ses .removeAttribute ("jzn" + name .substring (3 ));
446+ }
447+ }
448+ }
449+ }
450+ }
400451
401452}
0 commit comments