content: /**
   * Copyright 2007 DFKI GmbH.
   * All Rights Reserved.  Use is subject to license terms.
   * <p/>
   * This file is part of MARY TTS.
   * <p/>
   * MARY TTS is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Lesser General Public License as published by
   * the Free Software Foundation, version 3 of the License.
   * <p/>
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU Lesser General Public License for more details.
   * <p/>
   * You should have received a copy of the GNU Lesser General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  package marytts.server.http;
  
  import android.util.Log;
  
  import org.apache.http.Header;
  import org.apache.http.HttpEntityEnclosingRequest;
  import org.apache.http.HttpException;
  import org.apache.http.HttpRequest;
  import org.apache.http.HttpResponse;
  import org.apache.http.MethodNotSupportedException;
  import org.apache.http.nio.ContentDecoder;
  import org.apache.http.nio.ContentDecoderChannel;
  import org.apache.http.nio.FileContentDecoder;
  import org.apache.http.nio.IOControl;
  import org.apache.http.nio.entity.ConsumingNHttpEntity;
  import org.apache.http.nio.entity.ConsumingNHttpEntityTemplate;
  import org.apache.http.nio.entity.ContentListener;
  import org.apache.http.nio.protocol.SimpleNHttpRequestHandler;
  import org.apache.http.protocol.HttpContext;
  import org.apache.http.protocol.HttpRequestHandler;
  import org.apache.http.util.EntityUtils;
  
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.IOException;
  import java.nio.channels.FileChannel;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Locale;
  import java.util.Map;
  
  import marytts.server.Mary;
  import marytts.util.http.Address;
  
  //import org.apache.log4j.Logger;
  
  //import marytts.vocalizations.VocalizationSynthesizer;
  
  /**
   * Provides baseline functionality to process http requests to the Mary server.
   *
   * @author Oytun T&uuml;rk, Marc Schröder
   */
  public abstract class BaseHttpRequestHandler extends SimpleNHttpRequestHandler implements HttpRequestHandler {
      private final boolean useFileChannels = true;
  
      // protected static Logger logger;
      private int runningNumber = 1;
      private Map<String, Object[]> requestMap;
  
      public BaseHttpRequestHandler() {
          super();
  
          requestMap = Collections.synchronizedMap(new HashMap<String, Object[]>());
  
      }
  
  
      /**
       * The entry point of all HttpRequestHandlers.
       * When this method returns, the response is sent to the client.
       */
      @Override
      public void handle(final HttpRequest request, final HttpResponse response, final HttpContext context)
              throws HttpException, IOException {
          try {
              Header[] tmp = request.getHeaders("Host");
              Address serverAddressAtClient = getServerAddressAtClient(tmp[0].getValue());
              String uri = request.getRequestLine().getUri();
  
              String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH);
              if (!(method.equals("GET") || method.equals("POST"))) {
                  throw new MethodNotSupportedException(method + " method not supported");
              }
  
              String absPath = null;
              String query = null;
              if (uri != null && uri.length() > 0) {
                  if (!uri.startsWith("/")) {
                      throw new HttpException("Unexpected uri: '" + uri + "' does not start with a slash");
                  }
                  int q = uri.indexOf('?');
                  if (q == -1) {
                      absPath = uri;
                  } else {
                      absPath = uri.substring(0, q);
                      query = uri.substring(q + 1);
                  }
              }
              Map<String, String> queryItems = null;
              if (query != null && query.length() > 0) {
                  queryItems = MaryHttpServerUtils.toKeyValuePairs(query, true);
              }
  
              //Try and get parameters from different HTTP POST requests if you have not been able to do this above
              if (method.equals("POST") && queryItems == null
                      && request instanceof HttpEntityEnclosingRequest) {
                  try {
                      String postQuery = EntityUtils.toString(((HttpEntityEnclosingRequest) request).getEntity());
                      queryItems = MaryHttpServerUtils.toKeyValuePairs(postQuery, true);
                  } catch (Exception e) {
                      Log.d(Mary.LOG, "Cannot read post query", e);
                      MaryHttpServerUtils.errorInternalServerError(response, "Cannot read post query", e);
                  }
              }
  
              //Parse request and create appropriate response
              handleClientRequest(absPath, queryItems, response, serverAddressAtClient);
  
          } catch (RuntimeException re) {
              Log.w(Mary.LOG, "runtime exception in handle():", re);
          }
      }
  
      protected abstract void handleClientRequest(String absPath, Map<String, String> queryItems, HttpResponse response, Address serverAddressAtClient)
              throws IOException;
  
      protected Address getServerAddressAtClient(String fullHeader) {
          String fullAddress = fullHeader.trim();
          int index = fullAddress.indexOf('?');
  
          if (index > 0)
              fullAddress = fullAddress.substring(0, index);
  
          return new Address(fullAddress);
      }
  
  
      @Override
      public ConsumingNHttpEntity entityRequest(
              final HttpEntityEnclosingRequest request,
              final HttpContext context) throws HttpException, IOException {
          return new ConsumingNHttpEntityTemplate(
                  request.getEntity(),
                  new FileWriteListener(useFileChannels));
      }
  
  
      static class FileWriteListener implements ContentListener {
          private final File file;
          private final FileInputStream inputFile;
          private final FileChannel fileChannel;
          private final boolean useFileChannels;
          private long idx = 0;
  
          public FileWriteListener(boolean useFileChannels) throws IOException {
              this.file = File.createTempFile("tmp", ".tmp", null);
              this.inputFile = new FileInputStream(file);
              this.fileChannel = inputFile.getChannel();
              this.useFileChannels = useFileChannels;
          }
  
          @Override
          public void contentAvailable(ContentDecoder decoder, IOControl ioctrl)
                  throws IOException {
              long transferred;
              if (useFileChannels && decoder instanceof FileContentDecoder) {
                  transferred = ((FileContentDecoder) decoder).transfer(
                          fileChannel, idx, Long.MAX_VALUE);
              } else {
                  transferred = fileChannel.transferFrom(
                          new ContentDecoderChannel(decoder), idx, Long.MAX_VALUE);
              }
  
              if (transferred > 0)
                  idx += transferred;
          }
  
          @Override
          public void finished() {
              try {
                  inputFile.close();
              } catch (IOException ignored) {
              }
              try {
                  fileChannel.close();
              } catch (IOException ignored) {
              }
          }
      }
  
  
  }
  
  