/*
  * MappingResultのOptionに従ってVelocityHandlerに処理を任せる 1)"velocityUse"がfalse以外
  * 2)"velocityExtentions"と拡張子が一致する TODO mapping.setOption(key, value)を使って最適化
  */
 private boolean isVelocityUse(MappingResult mapping, String path) {
   if (path == null) {
     return false;
   }
   String velocityUse = (String) mapping.getOption(MappingResult.PARAMETER_VELOCITY_USE);
   if ("false".equalsIgnoreCase(velocityUse)) {
     return false;
   }
   if (path.endsWith("ph-loader.js")) { // 特別扱い
     setRequestAttribute(ATTRIBUTE_RESPONSE_CONTENT_TYPE, "application/javascript");
     return true;
   }
   String velocityExtentionsParam =
       (String) mapping.getOption(MappingResult.PARAMETER_VELOCITY_EXTENTIONS);
   String[] velocityExtentions = defaultVelocityExtentions;
   if (velocityExtentionsParam != null) {
     velocityExtentions = velocityExtentionsParam.split(",");
   }
   for (int i = 0; i < velocityExtentions.length; i++) {
     if (path.endsWith(velocityExtentions[i])) {
       return true;
     }
   }
   return false;
 }
 public void onRequestHeader(HeaderParser requestHeader) {
   /* portal機能の場合path情報にportal機能用の情報が着いている場合がある。
    * この情報は削除してproxy対象サーバにリクエスト
    */
   MappingResult mapping = proxyHandler.getRequestMapping();
   String path = mapping.getResolvePath();
   Matcher matcher = null;
   synchronized (portalPathInfoPattern) {
     matcher = portalPathInfoPattern.matcher(path);
   }
   StringBuffer sb = null;
   String portalPathInfo = null;
   if (matcher.find()) {
     sb = new StringBuffer();
     matcher.appendReplacement(sb, "");
     portalPathInfo = matcher.group(1);
     matcher.appendTail(sb);
     path = sb.toString();
     mapping.setResolvePath(path);
     requestHeader.setRequestUri(mapping.getResolvePath());
     proxyHandler.setRequestAttribute(PORTAL_PATHINFO_KEY, portalPathInfo);
   }
   /*
    * proxyでAuthrizationヘッダを付加する作戦の場合
   String basicAuthHeader = getBasicAuthHeader(mapping.isResolvedHttps(),mapping.getResolveServer());
   if (basicAuthHeader != null) {
   	requestHeader.addHeader(HeaderParser.WWW_AUTHRIZATION_HEADER, basicAuthHeader);
   	proxyHandler.setRequestAttribute(HeaderParser.WWW_AUTHRIZATION_HEADER, basicAuthHeader);
   }
    */
 }
  public void startResponseReqBody() {
    MappingResult mapping = getRequestMapping();
    if (Boolean.TRUE.equals(mapping.getOption("replay"))) {
      ReplayHelper helper = Config.getConfig().getReplayHelper();
      //			ByteBuffer[] body=bodyPage.getBuffer();
      if (helper.doReplay(this, null)) {
        return; // replayできた,bodyは消費されている
      }
    }

    if (response()) {
      responseEnd(); // TODO必要ないと思う
      return;
    }
  }
 private boolean isListing(MappingResult mapping) {
   Object listing = mapping.getOption(MappingResult.PARAMETER_FILE_LISTING);
   if (listing == null) {
     return defaultListing;
   }
   return Boolean.TRUE.toString().equalsIgnoreCase(listing.toString());
 }
 private String[] getWelcomeFiles(MappingResult mapping) {
   String welcomFiles = (String) mapping.getOption(MappingResult.PARAMETER_FILE_WELCOME_FILES);
   if (welcomFiles == null) {
     return defaultWelcomeFiles;
   }
   return welcomFiles.split(",");
 }
  // 存在確認済みのファイルをレスポンスする。
  private boolean sendFile(
      MappingResult mapping,
      File baseDirectory,
      String path,
      String ifModifiedSince,
      CacheBuffer asyncFile) {
    if (isVelocityUse(mapping, path)) {
      // TODO ちゃんとする
      mapping.setResolvePath(path); // 加工後のpathを設定
      mapping.setDesitinationFile(baseDirectory);
      forwardHandler(Mapping.VELOCITY_PAGE_HANDLER);
      asyncFile.close();
      return false; // 委譲
    }

    // String
    // ifModifiedSince=requestParser.getHeader(HeaderParser.IF_MODIFIED_SINCE_HEADER);
    Date ifModifiedSinceDate = HeaderParser.parseDateHeader(ifModifiedSince);
    long ifModifiedSinceTime = -1;
    if (ifModifiedSinceDate != null) {
      ifModifiedSinceTime = ifModifiedSinceDate.getTime();
    }
    FileInfo fileInfo = asyncFile.getFileInfo();
    long lastModifiedTime = fileInfo.getLastModified();
    String lastModified = HeaderParser.fomatDateHeader(new Date(lastModifiedTime));
    // ファイル日付として表現できる値には、誤差があるため、表現できる時刻を取得
    lastModifiedTime = HeaderParser.parseDateHeader(lastModified).getTime();
    if (ifModifiedSinceTime >= lastModifiedTime) {
      completeResponse("304");
      asyncFile.close();
      return true;
    }
    setHeader(HeaderParser.LAST_MODIFIED_HEADER, lastModified);
    long contentLength = getContentLength(fileInfo.length());
    setContentLength(contentLength);
    String contentDisposition =
        (String) getRequestAttribute(ATTRIBUTE_RESPONSE_CONTENT_DISPOSITION);
    if (contentDisposition != null) {
      setHeader(HeaderParser.CONTENT_DISPOSITION_HEADER, contentDisposition);
    }
    String contentType = getContentType(fileInfo.getCanonicalFile());
    setContentType(contentType);
    setStatusCode("200");
    responseBodyFromFile(asyncFile);
    return false;
  }
  private boolean response() {
    HeaderParser requestHeader = getRequestHeader();
    String ifModifiedSince = requestHeader.getHeader(HeaderParser.IF_MODIFIED_SINCE_HEADER);
    String selfPath = requestHeader.getRequestUri();

    MappingResult mapping = getRequestMapping();
    File file = (File) getRequestAttribute(ATTRIBUTE_RESPONSE_FILE);
    if (file != null) { // レスポンスするファイルが、直接指定された場合
      // FileCacheInfo fileCacheInfo=null;
      boolean useCache = true;
      if (getRequestAttribute(ATTRIBUTE_RESPONSE_FILE_NOT_USE_CACHE) == null) {
        useCache = false;
      }
      CacheBuffer asyncFile = CacheBuffer.open(file, useCache);
      FileInfo fileInfo = asyncFile.getFileInfo();
      if (!fileInfo.exists()) {
        logger.debug("Not found." + file.getAbsolutePath());
        completeResponse("404", "file not exists");
        asyncFile.close();
        return true;
      }
      return sendFile(mapping, null, null, ifModifiedSince, asyncFile);
    }

    String path = mapping.getResolvePath();
    try {
      path = URLDecoder.decode(path, "utf-8");
    } catch (UnsupportedEncodingException e) {
      logger.error("URLDecoder.decode error", e);
      throw new IllegalArgumentException("URLDecoder.decode error", e);
    }
    // クエリの削除
    int pos = path.indexOf('?');
    if (pos >= 0) {
      path = path.substring(0, pos);
    }

    File baseDirectory = mapping.getDestinationFile();
    CacheBuffer asyncFile = CacheBuffer.open(new File(baseDirectory, path));
    FileInfo info = asyncFile.getFileInfo();
    if (info.isError()) {
      logger.warn("fail to getCanonicalPath.");
      completeResponse("500", "fail to getCanonicalPath.");
      asyncFile.close();
      return true;
      // TODO トラバーサル
      // }else if(!info.isInBase()){
      // //トラバーサルされたら、loggingして404
      // logger.warn("traversal error.");
      // completeResponse("404","traversal error");
      // return true;
    } else if (!info.exists() || !info.canRead()) {
      asyncFile.close();
      logger.debug("Not found." + info.getCanonicalFile());
      completeResponse("404", "file not exists");
      return true;
    }
    // welcomefile処理
    String[] welcomeFiles = getWelcomeFiles(mapping);
    if (info.isDirectory() && welcomeFiles != null) {
      File dir = info.getCanonicalFile();
      asyncFile.close();
      asyncFile = welcomPage(dir, welcomeFiles);
      if (asyncFile == null) { // welcomfileが無かった
        //				completeResponse("404", "file not exists");
        return fileListIfNessesary(mapping, selfPath, dir, "/".equals(path));
      }
      info = asyncFile.getFileInfo();
      if (info.exists() && info.canRead() && !path.endsWith("/")) {
        asyncFile.close();
        // もし、URIが"/"で終わっていなかったら相対が解決できないので、リダイレクト
        ServerParser selfServer = requestHeader.getServer();
        StringBuilder sb = new StringBuilder();
        if (isSsl()) {
          sb.append("https://");
        } else {
          sb.append("http://");
        }
        sb.append(selfServer.toString());
        sb.append(selfPath);
        sb.append("/");
        setHeader(HeaderParser.LOCATION_HEADER, sb.toString());
        completeResponse("302");
        return true;
      }
    }
    if (info.isFile()) { // ファイルだったら
      return sendFile(mapping, baseDirectory, path, ifModifiedSince, asyncFile);
    }
    asyncFile.close();
    File dir = info.getCanonicalFile();
    return fileListIfNessesary(mapping, selfPath, dir, "/".equals(path));
  }
  public void onResponseHeader(HeaderParser responseHeader) {
    InjectionHelper helper = config.getInjectionHelper();

    MappingResult mapping = proxyHandler.getRequestMapping();
    HeaderParser requestHeader = proxyHandler.getRequestHeader();
    String WebAuthReplaceMark = requestHeader.getHeader(REPLACE_MARK_HEADER);
    String resolveUrl = mapping.getResolveUrl();
    if (WebAuthReplaceMark == null) {
      portalSession.endBasicProcess(resolveUrl);
    }
    String statusCode = responseHeader.getStatusCode();
    if ("401".equals(statusCode) /*&&injectContext==null*/) {
      mapping.getResolveDomain();
      String authentication = responseHeader.getHeader(HeaderParser.WWW_AUTHENTICATE_HEADER);
      if (authentication == null) {
        return;
      }
      Matcher matcher;
      synchronized (authenticationPattern) {
        matcher = authenticationPattern.matcher(authentication);
      }
      if (!matcher.find()) {
        return; // Digestはここでチェックあうと
      }
      String realm = matcher.group(1);

      // 自分の持っている代理ログイン情報で、domain,realmに合致するものはないか?
      String resolveDomain = mapping.getResolveDomain();
      CommissionAuth basicCommissionAuth = portalSession.getBasicAuth(resolveDomain, realm);
      if (WebAuthReplaceMark == null
          && !portalSession.startBasicProcess(resolveUrl, basicCommissionAuth)) {
        return;
      }
      if (basicCommissionAuth == null || basicCommissionAuth.isEnabled()) {
        String authrization = requestHeader.getHeader(HeaderParser.WWW_AUTHORIZATION_HEADER);
        if (WebAuthReplaceMark == null) { // ブラウザから直接出されたリクエスト
          responseHeader.setStatusCode("200");
          proxyHandler.removeResponseHeader(HeaderParser.WWW_AUTHENTICATE_HEADER);
          portalSession.putRealm(resolveUrl, realm);
          proxyHandler.setReplace(true);
          injectContext = helper.getReplaceContext("WebAuthReplace.html");
          proxyHandler.addResponseHeader(
              HeaderParser.CONTENT_TYPE_HEADER, "text/html; charset=utf-8");
          proxyHandler.addResponseHeader("Pragma", "no-cache");
          proxyHandler.addResponseHeader("Cache-Control", "no-cache");
          proxyHandler.addResponseHeader("Expires", "Thu, 01 Dec 1994 16:00:00 GMT");
        } else if (authrization != null) { // ajaxからuser/passをつけているのに401が返却された=>認証情報が無効
          responseHeader.setStatusCode("200");
          proxyHandler.removeResponseHeader(HeaderParser.WWW_AUTHENTICATE_HEADER);
          proxyHandler.addResponseHeader("WebAuthRealm", realm);
          proxyHandler.setReplace(true);
          injectContext = helper.getReplaceContext("WebAuthFail.html");
          proxyHandler.addResponseHeader(HeaderParser.CONTENT_TYPE_HEADER, "text/plain");
          proxyHandler.addResponseHeader("Pragma", "no-cache");
          proxyHandler.addResponseHeader("Cache-Control", "no-cache");
          proxyHandler.addResponseHeader("Expires", "Thu, 01 Dec 1994 16:00:00 GMT");
        }
      }
    } else if ("200".equals(statusCode) || "404".equals(statusCode)) {
      String contentType = responseHeader.getContentType();
      if (contentType != null && contentType.startsWith("text/html")) {
        injectContext = helper.getInsertContext("PortalInject.txt");
      }
    }
  }