package org.voovan.http.server.router;

import org.voovan.http.server.HttpRequest;
import org.voovan.http.server.HttpResponse;
import org.voovan.http.server.HttpRouter;
import org.voovan.http.server.MimeTools;
import org.voovan.http.server.exception.ResourceNotFound;
import org.voovan.tools.TDateTime;
import org.voovan.tools.TFile;
import org.voovan.tools.THash;
import org.voovan.tools.reflect.TReflect;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.Date;


/**
 * MIME 文件路由处理类
 * 
 * @author helyho
 *
 * Voovan Framework.
 * WebSite: https://github.com/helyho/Voovan
 * Licence: Apache v2 License
 */
public class MimeFileRouter implements HttpRouter {

	private String	rootPath;

	/**
	 * 构造函数
	 * @param rootPath 根路径
     */
	public MimeFileRouter(String rootPath) {
		this.rootPath = rootPath;
	}

	@Override
	public void process(HttpRequest request, HttpResponse response) throws Exception {
		String urlPath = request.protocol().getPath();
		if (MimeTools.isMimeFile(urlPath)) {
			// 获取扩展名
			String fileExtension = urlPath.substring(urlPath.lastIndexOf(".") + 1, urlPath.length());
			// 根据扩展名,设置 MIME 类型
			response.header().put("Content-Type", MimeTools.getMimeByFileExtension(fileExtension));
			// 转换请求Path 里的文件路劲分割符为系统默认分割符
			urlPath = urlPath.replaceAll("//", File.separator);
			// 拼装文件实际存储路径
			String filePath = rootPath + urlPath;
			File responseFile = new File(filePath);
			
			if (responseFile.exists()) {
				if(isNotModify(responseFile,request,response)){
					return ;
				}else{
					fillMimeFile(responseFile, request, response);
				}
			}else{
				throw new ResourceNotFound(urlPath);
			}
		}
	}
	
	/**
	 * 判断是否是304 not modify
	 * @param responseFile   响应文件
	 * @param request   HTTP 请求对象
	 * @param response  HTTP 响应对象
	 * @return 是否是304 not modify
	 * @throws ParseException 解析异常
	 */
	public boolean isNotModify(File responseFile,HttpRequest request,HttpResponse response) throws ParseException{
		//文件的修改日期
		Date fileModifyDate = new Date(responseFile.lastModified());

		//请求中的修改时间
		Date requestModifyDate = null;
		if(request.header().contain("If-Modified-Since")){
			try {
				requestModifyDate = TDateTime.parseToGMT(request.header().get("If-Modified-Since"));
			}catch(Exception e){
				requestModifyDate = null;
			}
		}

		//文件的 ETag
		String eTag = "\"" + THash.encryptMD5(Integer.toString(responseFile.hashCode()+fileModifyDate.hashCode())).toUpperCase() + "\"";

		//请求中的 ETag
		String requestETag = request.header().get("If-None-Match");
		
		//设置响应头 ETag
		response.header().put("ETag", eTag);
		//设置最后修改时间
		response.header().put("Last-Modified",TDateTime.formatToGMT(fileModifyDate));
		//设置缓存控制
		response.header().put("Cache-Control", "max-age=86400");
		//设置浏览器缓存超时控制
		response.header().put("Expires",TDateTime.formatToGMT(new Date(System.currentTimeMillis()+86400*1000)));
		
		//文件 hashcode 无变化,则返回304
		if(eTag.equals(requestETag)){
			setNotModifyResponse(response);
			return true;
		}
		//文件更新时间比请求时间大,则返回304
		if(fileModifyDate.equals(requestModifyDate)){
			setNotModifyResponse(response);
			return true; 
		} 
		return false;
	}
	
	/**
	 * 填充 mime 文件到 response
	 * @param responseFile   响应文件
	 * @param request   HTTP 请求对象
	 * @param response  HTTP 响应对象
	 * @throws IOException IO操作异常
	 */
	public void fillMimeFile(File responseFile,HttpRequest request,HttpResponse response) throws IOException {
		byte[] fileByte = null;
		long fileSize = TFile.getFileSize(responseFile);
		
		// 如果包含取一个范围内的文件内容进行处理,形似:Range: 0-800
		if (request.header().get("Range") != null && request.header().get("Range").contains("-")) {
			
			long beginPos=-1;
			long endPos=-1;
			
			String rangeStr = request.header().get("Range");
			rangeStr = rangeStr.replace("bytes=", "").trim();
			String[] ranges = rangeStr.split("-");
			
			//形似:Range: -800
			if(rangeStr.startsWith("-") && ranges.length==1){
				beginPos = fileSize - Long.parseLong(ranges[0]);
				endPos = fileSize;
			}
			//形似:Range: 800-
			else if(rangeStr.endsWith("-") && ranges.length==1){
				beginPos = Integer.parseInt(ranges[0]);
				endPos = fileSize;
			}
			//形似:Range: 0-800
			else if(ranges.length==2){
				beginPos = Long.parseLong(ranges[0]);
				endPos   = Long.parseLong(ranges[1]);
			}
			fileByte = TFile.loadFileFromSysPath(responseFile.getPath(), beginPos, endPos);
			response.header().put("Content-Range", "bytes " + rangeStr + "/" + fileSize);
		} else {
			fileByte = TFile.loadFileFromSysPath(responseFile.getPath());
		}

		if (fileByte != null) {
			try {
				ByteArrayOutputStream outputStream = TReflect.getFieldValue(response.body(), "outputStream");
				TReflect.setFieldValue(outputStream, "buf", fileByte);
				TReflect.setFieldValue(outputStream, "count", fileByte.length);
			}catch(ReflectiveOperationException e){
				throw new IOException(e);
			}
		}
		
	}
	
	/**
	 * 将响应报文设置称304
	 * @param response HTTP 响应对象
	 */
	public void setNotModifyResponse(HttpResponse response){
		response.protocol().setStatus(304);
		response.protocol().setStatusCode("Not Modified");
	}

}

@@ -4480,431 +4480,123 @@
 ;%0A%09%09
-%7D else %7B%0A%09%09%09fileByte = TFile.loadFileFromSysPath(responseFile.getPath());%0A%09%09%7D%0A%0A%09%09if (fileByte != null) %7B%0A%09%09%09try %7B%0A%09%09%09%09ByteArrayOutputStream outputStream = TReflect.getFieldValue(response.body(), %22outputStream%22);%0A%09%09%09%09TReflect.setFieldValue(outputStream, %22buf%22, fileByte);%0A%09%09%09%09TReflect.
+%09response.body().write(fileByte);%0A%0A%09%09%7D else %7B%0A%09%09%09response.body().changeToFile(respon
 se
-t
 Fi
-eldValue(outputStream, %22count%22, fileByte.length);%0A%09%09%09%7Dcatch(ReflectiveOperationException e)%7B%0A%09%09%09%09throw new IOException(e);%0A%09%09%09%7D%0A%09%09%7D%0A%09%09
+le.getCanonicalPath());%0A%09%09%7D
 %0A%09%7D%0A

优化 HTTP 报文解析