001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.writer; 020 021import java.io.IOException; 022import java.io.OutputStream; 023import java.util.zip.GZIPOutputStream; 024 025import javax.servlet.ServletOutputStream; 026import javax.servlet.http.HttpServletRequest; 027import javax.servlet.http.HttpServletResponse; 028 029import lucee.commons.lang.StringUtil; 030import lucee.runtime.Info; 031import lucee.runtime.PageContext; 032import lucee.runtime.PageContextImpl; 033import lucee.runtime.cache.legacy.CacheItem; 034import lucee.runtime.net.http.HttpServletResponseWrap; 035import lucee.runtime.net.http.ReqRspUtil; 036import lucee.runtime.op.Caster; 037 038/** 039 * Implementation of a JSpWriter 040 */ 041public class CFMLWriterImpl extends CFMLWriter { 042 043 private static final int BUFFER_SIZE = 100000; 044 private static final String VERSION = Info.getVersionAsString(); 045 private OutputStream out; 046 private HttpServletResponse response; 047 private boolean flushed; 048 private StringBuilder htmlHead; 049 private StringBuilder htmlBody; 050 private StringBuilder buffer=new StringBuilder(BUFFER_SIZE); 051 private boolean closed=false; 052 private boolean closeConn; 053 private boolean showVersion; 054 private boolean contentLength; 055 private CacheItem cacheItem; 056 private HttpServletRequest request; 057 private Boolean _allowCompression; 058 private PageContext pc; 059 060 /** 061 * constructor of the class 062 * @param response Response Object 063 * @param bufferSize buffer Size 064 * @param autoFlush do auto flush Content 065 */ 066 public CFMLWriterImpl(PageContext pc,HttpServletRequest request, HttpServletResponse response, int bufferSize, boolean autoFlush, boolean closeConn, boolean showVersion, boolean contentLength) { 067 super(bufferSize, autoFlush); 068 this.pc=pc; 069 this.request=request; 070 this.response=response; 071 this.autoFlush=autoFlush; 072 this.bufferSize=bufferSize; 073 this.closeConn=closeConn; 074 this.showVersion=showVersion; 075 this.contentLength=contentLength; 076 //this.allowCompression=allowCompression; 077 } 078 079 /* * 080 * constructor of the class 081 * @param response Response Object 082 * / 083 public JspWriterImpl(HttpServletResponse response) { 084 this(response, BUFFER_SIZE, false); 085 }*/ 086 087 private void _check() throws IOException { 088 if(autoFlush && buffer.length()>bufferSize) { 089 _flush(true); 090 } 091 } 092 093 /** 094 * @throws IOException 095 */ 096 protected void initOut() throws IOException { 097 if (out == null) { 098 out=getOutputStream(false); 099 //out=response.getWriter(); 100 } 101 } 102 103 104 /** 105 * @see javax.servlet.jsp.JspWriter#print(char[]) 106 */ 107 public void print(char[] arg) throws IOException { 108 buffer.append(arg); 109 _check(); 110 } 111 112 /** 113 * reset configuration of buffer 114 * @param bufferSize size of the buffer 115 * @param autoFlush does the buffer autoflush 116 * @throws IOException 117 */ 118 public void setBufferConfig(int bufferSize, boolean autoFlush) throws IOException { 119 this.bufferSize=bufferSize; 120 this.autoFlush=autoFlush; 121 _check(); 122 } 123 124 public void appendHTMLBody(String text) throws IOException { 125 126 if (htmlBody == null) 127 htmlBody = new StringBuilder(256); 128 129 htmlBody.append(text); 130 } 131 132 public void writeHTMLBody(String text) throws IOException { 133 134 if (flushed) throw new IOException("Page is already flushed"); 135 136 htmlBody = new StringBuilder(text); 137 } 138 139 public String getHTMLBody() throws IOException { 140 141 if (flushed) throw new IOException("Page is already flushed"); 142 143 return htmlBody == null ? "" : htmlBody.toString(); 144 } 145 146 public void flushHTMLBody() throws IOException { 147 148 if (htmlBody != null) { 149 150 buffer.append(htmlBody); 151 resetHTMLBody(); 152 } 153 } 154 155 /** 156 * @see lucee.runtime.writer.CFMLWriter#resetHTMLHead() 157 */ 158 public void resetHTMLBody() throws IOException { 159 if(flushed) throw new IOException("Page is already flushed"); 160 htmlBody = null; 161 } 162 163 /** 164 * 165 * @param text 166 * @throws IOException 167 */ 168 public void appendHTMLHead(String text) throws IOException { 169 170 if (flushed) throw new IOException("Page is already flushed"); 171 172 if (htmlHead == null) 173 htmlHead = new StringBuilder(256); 174 175 htmlHead.append(text); 176 } 177 178 public void writeHTMLHead(String text) throws IOException { 179 180 if (flushed) throw new IOException("Page is already flushed"); 181 182 htmlHead = new StringBuilder(text); 183 } 184 185 /** 186 * @see lucee.runtime.writer.CFMLWriter#getHTMLHead() 187 */ 188 public String getHTMLHead() throws IOException { 189 190 if (flushed) throw new IOException("Page is already flushed"); 191 192 return htmlHead == null ? "" : htmlHead.toString(); 193 } 194 195 public void flushHTMLHead() throws IOException { 196 197 if (htmlHead != null) { 198 199 buffer.append(htmlHead); 200 resetHTMLHead(); 201 } 202 } 203 204 /** 205 * @see lucee.runtime.writer.CFMLWriter#resetHTMLHead() 206 */ 207 public void resetHTMLHead() throws IOException { 208 if(flushed) throw new IOException("Page is already flushed"); 209 htmlHead =null; 210 } 211 212 /** 213 * just a wrapper function for ACF 214 * @throws IOException 215 */ 216 public void initHeaderBuffer() throws IOException{ 217 resetHTMLHead(); 218 } 219 220 221 /** 222 * @see java.io.Writer#write(char[], int, int) 223 */ 224 public void write(char[] cbuf, int off, int len) throws IOException { 225 buffer.append(cbuf,off,len); 226 _check(); 227 } 228 229 /** 230 * @see javax.servlet.jsp.JspWriter#clear() 231 */ 232 public void clear() throws IOException { 233 if (flushed) throw new IOException("Response buffer is already flushed"); 234 clearBuffer(); 235 } 236 237 /** 238 * @see javax.servlet.jsp.JspWriter#clearBuffer() 239 */ 240 public void clearBuffer() { 241 buffer=new StringBuilder(BUFFER_SIZE); 242 } 243 244 /** 245 * @see java.io.Writer#flush() 246 */ 247 public void flush() throws IOException { 248 flushBuffer(true); 249 // weil flushbuffer das out erstellt muss ich nicht mehr checken 250 out.flush(); 251 } 252 253 /** 254 * @see java.io.Writer#flush() 255 */ 256 private void _flush(boolean closeConn) throws IOException { 257 flushBuffer(closeConn); 258 // weil flushbuffer das out erstellt muss ich nicht mehr checken 259 out.flush(); 260 261 } 262 263 /** 264 * Flush the output buffer to the underlying character stream, without 265 * flushing the stream itself. This method is non-private only so that it 266 * may be invoked by PrintStream. 267 * @throws IOException 268 * @throws 269 */ 270 protected final void flushBuffer(boolean closeConn) throws IOException { 271 if(!flushed && closeConn) { 272 response.setHeader("connection", "close"); 273 if(showVersion)response.setHeader("Lucee-Version", VERSION); 274 275 } 276 initOut(); 277 byte[] barr = _toString(true).getBytes(ReqRspUtil.getCharacterEncoding(null,response)); 278 279 if(cacheItem!=null && cacheItem.isValid()) { 280 cacheItem.store(barr, flushed); 281 // writeCache(barr,flushed); 282 } 283 flushed = true; 284 out.write(barr); 285 286 buffer=new StringBuilder(BUFFER_SIZE); // to not change to clearBuffer, produce problem with CFMLWriterWhiteSpace.clearBuffer 287 } 288 289 290 291 private String _toString(boolean releaseHeadData) { 292 293 if (htmlBody == null && htmlHead == null) 294 return buffer.toString(); 295 296 String str = buffer.toString(); 297 298 if (htmlHead != null) { 299 300 int index = StringUtil.indexOfIgnoreCase(str, "</head>"); 301 if (index > -1) { 302 303 str = StringUtil.insertAt(str, htmlHead, index); 304 } 305 else { 306 307 index = StringUtil.indexOfIgnoreCase(str, "<head>") + 7; 308 if (index > 6) { 309 310 str = StringUtil.insertAt(str, htmlHead, index); 311 } 312 else { 313 314 str = htmlHead.append(str).toString(); 315 } 316 } 317 } 318 319 if (htmlBody != null) { 320 321 int index=StringUtil.indexOfIgnoreCase(str,"</body>"); 322 if (index > -1) { 323 324 str = StringUtil.insertAt(str, htmlBody, index); 325 } 326 else { 327 328 str += htmlBody.toString(); 329 } 330 } 331 332 if (releaseHeadData) { 333 htmlBody = null; 334 htmlHead = null; 335 } 336 337 return str; 338 } 339 340 /** 341 * @see java.lang.Object#toString() 342 */ 343 public String toString() { 344 return _toString(false); 345 } 346 347 348 349 350 351 /** 352 * @see java.io.Writer#close() 353 */ 354 public void close() throws IOException { 355 if (response == null || closed) return; 356 //boolean closeConn=true; 357 if(out==null) { 358 if(response.isCommitted()) { 359 closed=true; 360 return; 361 } 362 //print.out(_toString()); 363 byte[] barr = _toString(true).getBytes(ReqRspUtil.getCharacterEncoding(null,response)); 364 365 if(cacheItem!=null) { 366 cacheItem.store(barr, false); 367 // writeCache(barr,false); 368 } 369 370 if(closeConn)response.setHeader("connection", "close"); 371 if(showVersion)response.setHeader("Lucee-Version", VERSION); 372 boolean allowCompression; 373 if(barr.length<=512) 374 allowCompression=false; 375 else if(_allowCompression!=null) 376 allowCompression=_allowCompression.booleanValue(); 377 else 378 allowCompression=((PageContextImpl)pc).getAllowCompression(); 379 out = getOutputStream(allowCompression); 380 381 382 if(contentLength && !(out instanceof GZIPOutputStream))ReqRspUtil.setContentLength(response,barr.length); 383 384 out.write(barr); 385 out.flush(); 386 out.close(); 387 388 389 out = null; 390 } 391 else { 392 _flush(closeConn); 393 out.close(); 394 out = null; 395 } 396 closed = true; 397 } 398 399 400 private OutputStream getOutputStream(boolean allowCompression) throws IOException { 401 402 if (allowCompression){ 403 404 String encodings = ReqRspUtil.getHeader(request, "Accept-Encoding", ""); 405 if( encodings.indexOf("gzip")!=-1 ) { 406 boolean inline=HttpServletResponseWrap.get(); 407 if(!inline) { 408 ServletOutputStream os = response.getOutputStream(); 409 response.setHeader("Content-Encoding", "gzip"); 410 return new GZIPOutputStream(os); 411 } 412 } 413 } 414 return response.getOutputStream(); 415 } 416 417 418 419 /*private void writeCache(byte[] barr,boolean append) throws IOException { 420 cacheItem.store(barr, append); 421 //IOUtil.copy(new ByteArrayInputStream(barr), cacheItem.getResource().getOutputStream(append),true,true); 422 //MetaData.getInstance(cacheItem.getDirectory()).add(cacheItem.getName(), cacheItem.getRaw()); 423 }*/ 424 425 /** 426 * @see javax.servlet.jsp.JspWriter#getRemaining() 427 */ 428 public int getRemaining() { 429 return bufferSize - buffer.length(); 430 } 431 432 /** 433 * @see javax.servlet.jsp.JspWriter#newLine() 434 */ 435 public void newLine() throws IOException { 436 println(); 437 } 438 439 /** 440 * @see javax.servlet.jsp.JspWriter#print(boolean) 441 */ 442 public void print(boolean arg) throws IOException { 443 print(arg?new char[]{'t','r','u','e'}:new char[]{'f','a','l','s','e'}); 444 } 445 446 /** 447 * @see javax.servlet.jsp.JspWriter#print(char) 448 */ 449 public void print(char arg) throws IOException { 450 buffer.append(arg); 451 _check(); 452 } 453 454 /** 455 * @see javax.servlet.jsp.JspWriter#print(int) 456 */ 457 public void print(int arg) throws IOException { 458 _print(String.valueOf(arg)); 459 } 460 461 /** 462 * @see javax.servlet.jsp.JspWriter#print(long) 463 */ 464 public void print(long arg) throws IOException { 465 _print(String.valueOf(arg)); 466 467 } 468 469 /** 470 * @see javax.servlet.jsp.JspWriter#print(float) 471 */ 472 public void print(float arg) throws IOException { 473 _print(String.valueOf(arg)); 474 } 475 476 /** 477 * @see javax.servlet.jsp.JspWriter#print(double) 478 */ 479 public void print(double arg) throws IOException { 480 _print(String.valueOf(arg)); 481 } 482 483 /** 484 * @see javax.servlet.jsp.JspWriter#print(java.lang.String) 485 */ 486 public void print(String arg) throws IOException { 487 buffer.append(arg); 488 _check(); 489 } 490 491 /** 492 * @see javax.servlet.jsp.JspWriter#print(java.lang.Object) 493 */ 494 public void print(Object arg) throws IOException { 495 _print(String.valueOf(arg)); 496 } 497 498 /** 499 * @see javax.servlet.jsp.JspWriter#println() 500 */ 501 public void println() throws IOException { 502 _print("\n"); 503 504 } 505 506 /** 507 * @see javax.servlet.jsp.JspWriter#println(boolean) 508 */ 509 public void println(boolean arg) throws IOException { 510 print(arg?new char[]{'t','r','u','e','\n'}:new char[]{'f','a','l','s','e','\n'}); 511 } 512 513 /** 514 * @see javax.servlet.jsp.JspWriter#println(char) 515 */ 516 public void println(char arg) throws IOException { 517 print(new char[]{arg,'\n'}); 518 } 519 520 /** 521 * @see javax.servlet.jsp.JspWriter#println(int) 522 */ 523 public void println(int arg) throws IOException { 524 print(arg); 525 println(); 526 } 527 528 /** 529 * @see javax.servlet.jsp.JspWriter#println(long) 530 */ 531 public void println(long arg) throws IOException { 532 print(arg); 533 println(); 534 } 535 536 /** 537 * @see javax.servlet.jsp.JspWriter#println(float) 538 */ 539 public void println(float arg) throws IOException { 540 print(arg); 541 println(); 542 } 543 544 /** 545 * @see javax.servlet.jsp.JspWriter#println(double) 546 */ 547 public void println(double arg) throws IOException { 548 print(arg); 549 println(); 550 } 551 552 /** 553 * @see javax.servlet.jsp.JspWriter#println(char[]) 554 */ 555 public void println(char[] arg) throws IOException { 556 print(arg); 557 println(); 558 } 559 560 /** 561 * @see javax.servlet.jsp.JspWriter#println(java.lang.String) 562 */ 563 public void println(String arg) throws IOException { 564 _print(arg); 565 println(); 566 } 567 568 /** 569 * @see javax.servlet.jsp.JspWriter#println(java.lang.Object) 570 */ 571 public void println(Object arg) throws IOException { 572 print(arg); 573 println(); 574 } 575 576 /** 577 * @see java.io.Writer#write(char[]) 578 */ 579 public void write(char[] cbuf) throws IOException { 580 print(cbuf); 581 } 582 583 /** 584 * @see java.io.Writer#write(int) 585 */ 586 public void write(int c) throws IOException { 587 print(c); 588 } 589 590 /** 591 * @see java.io.Writer#write(java.lang.String, int, int) 592 */ 593 public void write(String str, int off, int len) throws IOException { 594 write(str.toCharArray(),off,len); 595 } 596 597 /** 598 * @see java.io.Writer#write(java.lang.String) 599 */ 600 public void write(String str) throws IOException { 601 buffer.append(str); 602 _check(); 603 } 604 605 /** 606 * @see lucee.runtime.writer.CFMLWriter#writeRaw(java.lang.String) 607 */ 608 public void writeRaw(String str) throws IOException { 609 _print(str); 610 } 611 612 /** 613 * @return Returns the flushed. 614 */ 615 public boolean isFlushed() { 616 return flushed; 617 } 618 619 public void setClosed(boolean closed) { 620 this.closed=closed; 621 } 622 623 private void _print(String arg) throws IOException { 624 buffer.append(arg); 625 _check(); 626 } 627 628 /** 629 * @see lucee.runtime.writer.CFMLWriter#getResponseStream() 630 */ 631 public OutputStream getResponseStream() throws IOException { 632 initOut(); 633 return out; 634 } 635 636 public void doCache(lucee.runtime.cache.legacy.CacheItem ci) { 637 this.cacheItem=ci; 638 } 639 640 /** 641 * @return the cacheResource 642 */ 643 public CacheItem getCacheItem() { 644 return cacheItem; 645 } 646 647 648 // only for compatibility to other vendors 649 public String getString() { 650 return toString(); 651 } 652 653 @Override 654 public void setAllowCompression(boolean allowCompression) { 655 this._allowCompression=Caster.toBoolean(allowCompression); 656 } 657 658 659 660}