package net.sf.sahi.response; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import net.sf.sahi.stream.filter.ReadWriteThread; import net.sf.sahi.stream.filter.StreamFilter; import net.sf.sahi.stream.filter.StreamFilterInputStream; import net.sf.sahi.util.Utils; /** * Sahi - Web Automation and Test Tool * <p/> * Copyright 2006 V Narayan Raman * <p/> * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * <p/> * http://www.apache.org/licenses/LICENSE-2.0 * <p/> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ public class StreamingHttpResponse extends HttpResponse { protected BufferedInputStream in; private List<StreamFilter> filters = new ArrayList<StreamFilter>(); private static ExecutorService pool = Executors.newCachedThreadPool(); StreamingHttpResponse() { } public StreamingHttpResponse(InputStream in, HttpURLConnection connection) throws IOException { if (!(in instanceof BufferedInputStream)) { this.in = new BufferedInputStream(in, Utils.BUFFER_SIZE); } else { this.in = (BufferedInputStream) in; } Map<String, List<String>> connheaders = connection.getHeaderFields(); setHeaders(connection); List<String> firstLines = connheaders.get(null); // can have multiple lines, especially if there are unknown connection keys. // choose the one with HTTP in it. if (firstLines != null) { for (Iterator<String> iterator = firstLines.iterator(); iterator.hasNext(); ) { String line = iterator.next(); if (line.indexOf("HTTP") != -1) firstLine = line; } } setContentLength(connection.getContentLength()); } public StreamingHttpResponse(HttpResponse response) { copyFrom(response); this.in = new BufferedInputStream(new ByteArrayInputStream(response.data()), Utils.BUFFER_SIZE); } private void setHeaders(HttpURLConnection connection) { for (int i = 1; true; i++) { String key = connection.getHeaderFieldKey(i); if (key == null) break; addHeader(key, connection.getHeaderField(i)); } } public byte[] data() { return new byte[0]; } int getModifiedContentLength() { return contentLength(); } public void modifyHeaders(boolean isKeepAlive) { proxyKeepAlive(isKeepAlive); modifyHeadersViaFilters(this); resetRawHeaders(); } public void sendBody(OutputStream out) throws IOException { OutputStream outputStreamToBrowser = new BufferedOutputStream(out, Utils.BUFFER_SIZE); int length = contentLength(); pipe(in, outputStreamToBrowser, length); outputStreamToBrowser.flush(); } public void copyFrom(StreamingHttpResponse orig) { this.in = orig.in; super.copyFrom(orig); } private InputStream applyFilters(InputStream in) { Iterator<StreamFilter> iter = this.filters.iterator(); while (iter.hasNext()) { StreamFilter filter = iter.next(); // in = filter.filter(in); in = new StreamFilterInputStream(in, filter); // Contributed by [Richard Li] } return in; } private void modifyHeadersViaFilters(HttpResponse response) { Iterator<StreamFilter> iter = this.filters.iterator(); while (iter.hasNext()) { StreamFilter filter = iter.next(); try { filter.modifyHeaders(response); } catch (IOException e) { e.printStackTrace(); } } } public void addFilter(StreamFilter filter) { this.filters.add(filter); } public List<StreamFilter> getFilters() { return filters; } private void pipe(InputStream in, OutputStream out, int contentLength) throws IOException { InputStream pin = new PipedInputStream(); OutputStream pout = new PipedOutputStream((PipedInputStream) pin); pin = applyFilters(pin); pool.execute(new ReadWriteThread(in, pout, contentLength, true, "Reader")); Future<?> future = pool.submit(new ReadWriteThread(pin, out, "Writer")); // Join using future.get() so that connection is closed only after stream has been written to. try { future.get(); } catch (Exception e1) { e1.printStackTrace(); } } public void cleanUp() { try { in.close(); } catch (IOException e) { // e.printStackTrace(); } } }