导入学子商城webapp资源后访问其首页,发现页面无法正常显示.
浏览器F12跟踪请求和响应的交互发现两个问题:
1:我们仅发送了两个响应头(Content-Length和Content-Type).
虽然目前仅需要这两个头,但是服务端实际可以根据处理情况设置需要发送其他响应头
2:Content-Type
的值是固定的"text/html",这导致浏览器请求到该资源后无法正确
理解该资源因此没有发挥出实际作用.分两个版本解决这两个问题
此版本解决响应头可根据设置进行发送.实现:
- 在HttpServletResponse中添加一个Map类型的属性用于保存所有要发送的响应头
Mapheaders - 修改发送响应头的方法
sendHeaders
中的逻辑,将固定发送两个头的操作改为遍历
headers这个Map,将所有要发送的响应头逐个发送- 只需要在发送前根据处理情况向headers中put要发送的响应头即可.这个工作需要
- 在响应对象中添加一个方法:
addHeader
,将要发送的响应头存入headers中- 在DispatcherServlet处理请求环节调用
addHeader
存放要发送的响应头即可http://localhost:8088/TeduStore/index.html
package com.birdboot.core;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 主启动类
*/
public class BirdBootApplication {
private ServerSocket serverSocket;
public BirdBootApplication(){
try {
System.out.println("正在启动服务端...");
serverSocket = new ServerSocket(8088);
System.out.println("服务端启动完毕!");
} catch (IOException e) {
e.printStackTrace();
}
}
public void start(){
try {
while(true) {
System.out.println("等待客户端链接...");
Socket socket = serverSocket.accept();
System.out.println("一个客户端链接了!");
//启动一个线程处理该客户端交互
ClientHandler handler = new ClientHandler(socket);
Thread t = new Thread(handler);
t.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
BirdBootApplication application = new BirdBootApplication();
application.start();
}
}
package com.birdboot.core;
import com.birdboot.http.HttpServletRequest;
import com.birdboot.http.HttpServletResponse;
import java.io.*;
import java.net.Socket;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* 该线程任务负责与指定的客户端进行HTTP交互
* HTTP协议要求浏览器与服务端采取"一问一答"的模式。对此,这里的处理流程分为三步:
* 1:解析请求
* 2:处理请求
* 3:发送响应
*/
public class ClientHandler implements Runnable {
private Socket socket;
public ClientHandler(Socket socket) {
this.socket = socket;
}
public void run() {
try {
//1 解析请求
HttpServletRequest request = new HttpServletRequest(socket);
HttpServletResponse response = new HttpServletResponse(socket);
//2 处理请求
//V8改造:将处理请求的操作移动到DispatcherServlet的service方法中并调用
DispatcherServlet servlet = new DispatcherServlet();
servlet.service(request,response);
//3 发送响应
response.response();
} catch (IOException e) {
e.printStackTrace();
} finally {
//V8新增内容:交互后与浏览器断开连接
//HTTP协议要求浏览器与服务端交互完毕后要断开连接
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.birdboot.core;
import com.birdboot.http.HttpServletRequest;
import com.birdboot.http.HttpServletResponse;
import java.io.File;
import java.net.URISyntaxException;
/**
* V8新增内容:
* 该类是SpringMVC框架与Tomcat整合时的一个关键类
* Tomcat处理业务原生的都是调用继承了HttpServlet的类来完成,此时需要进行很多配置
* 以及使用时要作很多重复性劳动。
* SpringMVC框架提供的该类也是继承了HttpServlet的,使用它来接收处理请求的工作。
*/
public class DispatcherServlet {
private static File baseDir;//类加载路径
private static File staticDir;//类加载路径下的static目录
static{
try {
//定位当前项目的类加载路径
baseDir = new File(
DispatcherServlet.class.getClassLoader().getResource(".").toURI()
);
//定位类加载路径下的static目录
staticDir = new File(baseDir, "static");
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
/**
* service方法实际上是当我们继承了HttpServlet后必须重写的方法
* 该方法要求接收两个参数:请求对象与响应对象。
* Tomcat在处理请求时就是调用某个Servlet的service方法并将请求与响应对象传入
* 来让其完成处理工作的。
*/
public void service(HttpServletRequest request, HttpServletResponse response){
//获取请求的抽象路径
String path = request.getUri();
System.out.println(path);
File file = new File(staticDir, path);
if(file.isFile()){
//由于响应对象中状态代码和描述默认值为200,OK因此正确情况下不用再设置
response.setContentFile(file);
//设置响应头
response.addHeader("Content-Type","text/html");
response.addHeader("Content-Length",file.length()+"");
response.addHeader("Server","BirdServer");
}else{
response.setStatusCode(404);
response.setStatusReason("NotFound");
file = new File(staticDir,"404.html");
response.setContentFile(file);
response.addHeader("Content-Type","text/html");
response.addHeader("Content-Length",file.length()+"");
response.addHeader("Server","BirdServer");
}
}
}
package com.birdboot.http;
import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* V7新增内容:
* 响应对象
* 该类的每一个实例用于表示服务端给客户端发送的一个HTTP的响应
* HTTP协议要求一个响应由三部分构成:状态行,响应头,响应正文
*/
public class HttpServletResponse {
private Socket socket;
//状态行相关信息
private int statusCode = 200;//状态代码
private String statusReason = "OK";//状态描述
//响应头相关信息 key:响应头的名字 value:响应头的值
private Map<String,String> headers = new HashMap<>();
//响应正文相关信息
private File contentFile;//响应正文对应的实体文件
public HttpServletResponse(Socket socket){
this.socket = socket;
}
/**
* 该方法用于将当前响应对象内容以标准的HTTP响应格式发送给客户端
*/
public void response() throws IOException {
//3.1发送状态行
sendStatusLine();
//3.2发送响应头
sendHeaders();
//3.3发送响应正文
sendContent();
}
//发送状态行
private void sendStatusLine() throws IOException {
println("HTTP/1.1"+" "+statusCode+" "+statusReason);
}
//发送响应头
private void sendHeaders() throws IOException {
/*
遍历headers将所有待发送的响应头发送给浏览器
headers
key value
Content-Type text/html
Content-Length 42123
Server BirdServer
... ...
*/
Set<Map.Entry<String,String>> entrySet = headers.entrySet();
for(Map.Entry<String,String> e : entrySet){
String name = e.getKey();
String value = e.getValue();
println(name+": "+value);
}
//单独发送回车+换行,表示响应头发送完毕
println("");
}
//发送响应正文
private void sendContent() throws IOException {
FileInputStream fis = new FileInputStream(contentFile);
OutputStream out = socket.getOutputStream();
byte[] buf = new byte[1024*10];//10kb
int d;//记录每次实际读取的数据量
while( (d = fis.read(buf)) !=-1){
out.write(buf,0,d);
}
}
/**
* V7:将ClientHandler中发送响应的工作全部移动到这里,println方法也是。
* 向客户端发送一行字符串
* @param line
*/
private void println(String line) throws IOException {
OutputStream out = socket.getOutputStream();
byte[] data = line.getBytes(StandardCharsets.ISO_8859_1);
out.write(data);
out.write(13);//发送回车符
out.write(10);//发送换行符
}
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
public String getStatusReason() {
return statusReason;
}
public void setStatusReason(String statusReason) {
this.statusReason = statusReason;
}
public File getContentFile() {
return contentFile;
}
public void setContentFile(File contentFile) {
this.contentFile = contentFile;
}
/**
* 添加一个响应头
* @param name
* @param value
*/
public void addHeader(String name,String value){
headers.put(name,value);
}
}
package com.birdboot.http;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
/**
* V4:新增内容
* 请求对象
* 该类的每一个实例用于表示浏览器发送过来的一个HTTP请求
* HTTP协议要求请求的格式由三部分构成:请求行,消息头,消息正文
*/
public class HttpServletRequest {
private Socket socket;
//请求行相关信息
private String method;//请求方式
private String uri;//抽象路径
private String protocol;//协议版本
//消息头相关信息 key:消息头名字 value:消息头对应的值
private Map<String,String> headers = new HashMap<>();
public HttpServletRequest(Socket socket) throws IOException {
this.socket = socket;
//1.1解析请求行
parseRequestLine();
//1.2解析消息头
parseHeaders();
//1.3解析消息正文
parseContent();
}
//解析请求行
private void parseRequestLine() throws IOException {
String line = readLine();
System.out.println("请求行:"+line);
//将请求行按照空格("\s"在正则表达式中表示一个空白字符,包含空格)拆分为三部分
String[] data = line.split("\\s");
method = data[0];
uri = data[1];
protocol = data[2];
System.out.println("method:"+method);
System.out.println("uri:"+uri);
System.out.println("protocol:"+protocol);
}
//解析消息头
private void parseHeaders() throws IOException {
while(true) {
String line = readLine();
if(line.isEmpty()){//如果读取到了空行
break;
}
System.out.println("消息头:" + line);
String[] data = line.split(":\\s");
headers.put(data[0],data[1]);
}
System.out.println("headers:"+headers);
}
//解析消息正文
private void parseContent(){}
/**
* 通过socket获取的输入流读取客户端发送过来的一行字符串
* @return
*/
private String readLine() throws IOException {//通常被重用的代码不自己处理异常
//对一个socket实例调用多次getInputStream()返回的始终是同一条输入流。而输出流也是如此
InputStream in = socket.getInputStream();
int d;
char pre='a',cur='a';//pre表示上次读取的字符,cur表示本次读取的字符
StringBuilder builder = new StringBuilder();//保存读取后的所有字符
while((d = in.read())!=-1){
cur = (char)d;//本次读取的字符
if(pre==13 && cur==10){//是否连续读取到了回车+换行
break;
}
builder.append(cur);//将本次读取的字符拼接
pre=cur;//在进行下次读取前,将本次读取的字符保存到"上次读取的字符"中
}
return builder.toString().trim();
}
public String getMethod() {
return method;
}
public String getUri() {
return uri;
}
public String getProtocol() {
return protocol;
}
/**
* 根据给定的消息头的名字获取对应消息头的值
* @param name
* @return
*/
public String getHeader(String name) {
return headers.get(name);
}
}
实现
HttpServletResponse
响应正确的MIME类型,即:Content-Type
的值这样一来,服务端就可以正确响应浏览器请求的任何资源了,使得浏览器可以正确显示内容
实现:
1:将原DispatcherServlet中设置响应头Content-Type
和Content-Length
的工作移动到HttpServletResponse
的设置响应正文方法setContentFile
中.
原因:一个响应中只要包含正文就应当包含说明正文信息的两个头Content-Type
和Content-Length
.因此我们完全可以在设置正文的时候自动设置这两个头.
这样做的好处是将来设置完正文(调用setContentFile)后无需再单独设置这两个头了.
2:使用MimetypesFileTypeMap
的方法getContentType
按照正文文件分析MIME类型并设置到
响应头Content-Type
上
package com.birdboot.core;
import com.birdboot.http.HttpServletRequest;
import com.birdboot.http.HttpServletResponse;
import java.io.File;
import java.net.URISyntaxException;
/**
* V8新增内容:
* 该类是SpringMVC框架与Tomcat整合时的一个关键类
* Tomcat处理业务原生的都是调用继承了HttpServlet的类来完成,此时需要进行很多配置
* 以及使用时要作很多重复性劳动。
* SpringMVC框架提供的该类也是继承了HttpServlet的,使用它来接收处理请求的工作。
*/
public class DispatcherServlet {
private static File baseDir;//类加载路径
private static File staticDir;//类加载路径下的static目录
static{
try {
//定位当前项目的类加载路径
baseDir = new File(
DispatcherServlet.class.getClassLoader().getResource(".").toURI()
);
//定位类加载路径下的static目录
staticDir = new File(baseDir, "static");
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
/**
* service方法实际上是当我们继承了HttpServlet后必须重写的方法
* 该方法要求接收两个参数:请求对象与响应对象。
* Tomcat在处理请求时就是调用某个Servlet的service方法并将请求与响应对象传入
* 来让其完成处理工作的。
*/
public void service(HttpServletRequest request, HttpServletResponse response){
//获取请求的抽象路径
String path = request.getUri();
System.out.println(path);
File file = new File(staticDir, path);
if(file.isFile()){
//由于响应对象中状态代码和描述默认值为200,OK因此正确情况下不用再设置
response.setContentFile(file);
//设置响应头
response.addHeader("Server","BirdServer");
}else{
response.setStatusCode(404);
response.setStatusReason("NotFound");
file = new File(staticDir,"404.html");
response.setContentFile(file);
response.addHeader("Server","BirdServer");
}
}
}
package com.birdboot.http;
import javax.activation.MimetypesFileTypeMap;
import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* V7新增内容:
* 响应对象
* 该类的每一个实例用于表示服务端给客户端发送的一个HTTP的响应
* HTTP协议要求一个响应由三部分构成:状态行,响应头,响应正文
*/
public class HttpServletResponse {
private static MimetypesFileTypeMap mftm = new MimetypesFileTypeMap();
private Socket socket;
//状态行相关信息
private int statusCode = 200;//状态代码
private String statusReason = "OK";//状态描述
//响应头相关信息 key:响应头的名字 value:响应头的值
private Map<String,String> headers = new HashMap<>();
//响应正文相关信息
private File contentFile;//响应正文对应的实体文件
public HttpServletResponse(Socket socket){
this.socket = socket;
}
/**
* 该方法用于将当前响应对象内容以标准的HTTP响应格式发送给客户端
*/
public void response() throws IOException {
//3.1发送状态行
sendStatusLine();
//3.2发送响应头
sendHeaders();
//3.3发送响应正文
sendContent();
}
//发送状态行
private void sendStatusLine() throws IOException {
println("HTTP/1.1"+" "+statusCode+" "+statusReason);
}
//发送响应头
private void sendHeaders() throws IOException {
/*
遍历headers将所有待发送的响应头发送给浏览器
headers
key value
Content-Type text/html
Content-Length 42123
Server BirdServer
... ...
*/
Set<Map.Entry<String,String>> entrySet = headers.entrySet();
for(Map.Entry<String,String> e : entrySet){
String name = e.getKey();
String value = e.getValue();
println(name+": "+value);
}
//单独发送回车+换行,表示响应头发送完毕
println("");
}
//发送响应正文
private void sendContent() throws IOException {
FileInputStream fis = new FileInputStream(contentFile);
OutputStream out = socket.getOutputStream();
byte[] buf = new byte[1024*10];//10kb
int d;//记录每次实际读取的数据量
while( (d = fis.read(buf)) !=-1){
out.write(buf,0,d);
}
}
/**
* V7:将ClientHandler中发送响应的工作全部移动到这里,println方法也是。
* 向客户端发送一行字符串
* @param line
*/
private void println(String line) throws IOException {
OutputStream out = socket.getOutputStream();
byte[] data = line.getBytes(StandardCharsets.ISO_8859_1);
out.write(data);
out.write(13);//发送回车符
out.write(10);//发送换行符
}
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
public String getStatusReason() {
return statusReason;
}
public void setStatusReason(String statusReason) {
this.statusReason = statusReason;
}
public File getContentFile() {
return contentFile;
}
/**
* 设置响应正文对应的实体文件,该方法中会自动根据该文件添加对应的两个响应头:
* Content-Type和Content-Length
* @param contentFile
*/
public void setContentFile(File contentFile) {
this.contentFile = contentFile;
addHeader("Content-Type",mftm.getContentType(contentFile));
addHeader("Content-Length",contentFile.length()+"");
}
/**
* 添加一个响应头
* @param name
* @param value
*/
public void addHeader(String name,String value){
headers.put(name,value);
}
}
application/vnd.lotus-1-2-3 123 123
text/vnd.in3d.3dml 3dml 3DML
image/x-3ds 3ds 3DS
video/3gpp2 3g2 3G2
video/3gpp 3gp 3GP
application/x-7z-compressed 7z 7Z
application/x-authorware-bin aab AAB
audio/x-aac aac AAC
application/x-authorware-map aam AAM
application/x-authorware-seg aas AAS
audio/x-mpeg abs ABS
application/x-abiword abw ABW
application/pkix-attr-cert ac AC
application/vnd.americandynamics.acc acc ACC
application/x-ace-compressed ace ACE
application/vnd.acucobol acu ACU
application/vnd.acucorp acutc ACUTC
audio/adpcm adp ADP
application/vnd.audiograph aep AEP
application/x-font-type1 afm AFM
application/vnd.ibm.modcap afp AFP
application/vnd.ahead.space ahead AHEAD
application/postscript ai AI
audio/x-aiff aif AIF
audio/x-aiff aifc AIFC
audio/x-aiff aiff AIFF
application/x-aim aim AIM
application/vnd.adobe.air-application-installer-package+zip air AIR
application/vnd.dvb.ait ait AIT
application/vnd.amiga.ami ami AMI
application/annodex anx ANX
application/vnd.android.package-archive apk APK
text/cache-manifest appcache APPCACHE
application/x-ms-application application APPLICATION
application/vnd.lotus-approach apr APR
application/x-freearc arc ARC
image/x-jg art ART
application/pgp-signature asc ASC
video/x-ms-asf asf ASF
text/x-asm asm ASM
application/vnd.accpac.simply.aso aso ASO
video/x-ms-asf asx ASX
application/vnd.acucorp atc ATC
application/atom+xml atom ATOM
application/atomcat+xml atomcat ATOMCAT
application/atomsvc+xml atomsvc ATOMSVC
application/vnd.antix.game-component atx ATX
audio/basic au AU
video/x-msvideo avi AVI
video/x-rad-screenplay avx AVX
application/applixware aw AW
audio/annodex axa AXA
video/annodex axv AXV
application/vnd.airzip.filesecure.azf azf AZF
application/vnd.airzip.filesecure.azs azs AZS
application/vnd.amazon.ebook azw AZW
application/x-msdownload bat BAT
application/x-bcpio bcpio BCPIO
application/x-font-bdf bdf BDF
application/vnd.syncml.dm+wbxml bdm BDM
application/vnd.realvnc.bed bed BED
application/vnd.fujitsu.oasysprs bh2 BH2
application/octet-stream bin BIN
application/x-blorb blb BLB
application/x-blorb blorb BLORB
application/vnd.bmi bmi BMI
image/bmp bmp BMP
text/html body BODY
application/vnd.framemaker book BOOK
application/vnd.previewsystems.box box BOX
application/x-bzip2 boz BOZ
application/octet-stream bpk BPK
image/prs.btif btif BTIF
application/x-bzip bz BZ
application/x-bzip2 bz2 BZ2
text/x-c c C
application/vnd.cluetrust.cartomobile-config c11amc C11AMC
application/vnd.cluetrust.cartomobile-config-pkg c11amz C11AMZ
application/vnd.clonk.c4group c4d C4D
application/vnd.clonk.c4group c4f C4F
application/vnd.clonk.c4group c4g C4G
application/vnd.clonk.c4group c4p C4P
application/vnd.clonk.c4group c4u C4U
application/vnd.ms-cab-compressed cab CAB
audio/x-caf caf CAF
application/vnd.tcpdump.pcap cap CAP
application/vnd.curl.car car CAR
application/vnd.ms-pki.seccat cat CAT
application/x-cbr cb7 CB7
application/x-cbr cba CBA
application/x-cbr cbr CBR
application/x-cbr cbt CBT
application/x-cbr cbz CBZ
text/x-c cc CC
application/x-director cct CCT
application/ccxml+xml ccxml CCXML
application/vnd.contact.cmsg cdbcmsg CDBCMSG
application/x-cdf cdf CDF
application/vnd.mediastation.cdkey cdkey CDKEY
application/cdmi-capability cdmia CDMIA
application/cdmi-container cdmic CDMIC
application/cdmi-domain cdmid CDMID
application/cdmi-object cdmio CDMIO
application/cdmi-queue cdmiq CDMIQ
chemical/x-cdx cdx CDX
application/vnd.chemdraw+xml cdxml CDXML
application/vnd.cinderella cdy CDY
application/pkix-cert cer CER
application/x-cfs-compressed cfs CFS
image/cgm cgm CGM
application/x-chat chat CHAT
application/vnd.ms-htmlhelp chm CHM
application/vnd.kde.kchart chrt CHRT
chemical/x-cif cif CIF
application/vnd.anser-web-certificate-issue-initiation cii CII
application/vnd.ms-artgalry cil CIL
application/vnd.claymore cla CLA
application/java class CLASS
application/vnd.crick.clicker.keyboard clkk CLKK
application/vnd.crick.clicker.palette clkp CLKP
application/vnd.crick.clicker.template clkt CLKT
application/vnd.crick.clicker.wordbank clkw CLKW
application/vnd.crick.clicker clkx CLKX
application/x-msclip clp CLP
application/vnd.cosmocaller cmc CMC
chemical/x-cmdf cmdf CMDF
chemical/x-cml cml CML
application/vnd.yellowriver-custom-menu cmp CMP
image/x-cmx cmx CMX
application/vnd.rim.cod cod COD
application/x-msdownload com COM
text/plain conf CONF
application/x-cpio cpio CPIO
text/x-c cpp CPP
application/mac-compactpro cpt CPT
application/x-mscardfile crd CRD
application/pkix-crl crl CRL
application/x-x509-ca-cert crt CRT
application/vnd.rig.cryptonote cryptonote CRYPTONOTE
application/x-csh csh CSH
chemical/x-csml csml CSML
application/vnd.commonspace csp CSP
text/css css CSS
application/x-director cst CST
text/csv csv CSV
application/cu-seeme cu CU
text/vnd.curl curl CURL
application/prs.cww cww CWW
application/x-director cxt CXT
text/x-c cxx CXX
model/vnd.collada+xml dae DAE
application/vnd.mobius.daf daf DAF
application/vnd.dart dart DART
application/vnd.fdsn.seed dataless DATALESS
application/davmount+xml davmount DAVMOUNT
application/docbook+xml dbk DBK
application/x-director dcr DCR
text/vnd.curl.dcurl dcurl DCURL
application/vnd.oma.dd2+xml dd2 DD2
application/vnd.fujixerox.ddd ddd DDD
application/x-debian-package deb DEB
text/plain def DEF
application/octet-stream deploy DEPLOY
application/x-x509-ca-cert der DER
application/vnd.dreamfactory dfac DFAC
application/x-dgc-compressed dgc DGC
image/bmp dib DIB
text/x-c dic DIC
application/x-director dir DIR
application/vnd.mobius.dis dis DIS
application/octet-stream dist DIST
application/octet-stream distz DISTZ
image/vnd.djvu djv DJV
image/vnd.djvu djvu DJVU
application/x-msdownload dll DLL
application/x-apple-diskimage dmg DMG
application/vnd.tcpdump.pcap dmp DMP
application/octet-stream dms DMS
application/vnd.dna dna DNA
application/msword doc DOC
application/vnd.ms-word.document.macroenabled.12 docm DOCM
application/vnd.openxmlformats-officedocument.wordprocessingml.document docx DOCX
application/msword dot DOT
application/vnd.ms-word.template.macroenabled.12 dotm DOTM
application/vnd.openxmlformats-officedocument.wordprocessingml.template dotx DOTX
application/vnd.osgi.dp dp DP
application/vnd.dpgraph dpg DPG
audio/vnd.dra dra DRA
text/prs.lines.tag dsc DSC
application/dssc+der dssc DSSC
application/x-dtbook+xml dtb DTB
application/xml-dtd dtd DTD
audio/vnd.dts dts DTS
audio/vnd.dts.hd dtshd DTSHD
application/octet-stream dump DUMP
video/x-dv dv DV
video/vnd.dvb.file dvb DVB
application/x-dvi dvi DVI
model/vnd.dwf dwf DWF
image/vnd.dwg dwg DWG
image/vnd.dxf dxf DXF
application/vnd.spotfire.dxp dxp DXP
application/x-director dxr DXR
audio/vnd.nuera.ecelp4800 ecelp4800 ECELP4800
audio/vnd.nuera.ecelp7470 ecelp7470 ECELP7470
audio/vnd.nuera.ecelp9600 ecelp9600 ECELP9600
application/ecmascript ecma ECMA
application/vnd.novadigm.edm edm EDM
application/vnd.novadigm.edx edx EDX
application/vnd.picsel efif EFIF
application/vnd.pg.osasli ei6 EI6
application/octet-stream elc ELC
application/x-msmetafile emf EMF
message/rfc822 eml EML
application/emma+xml emma EMMA
application/x-msmetafile emz EMZ
audio/vnd.digital-winds eol EOL
application/vnd.ms-fontobject eot EOT
application/postscript eps EPS
application/epub+zip epub EPUB
application/vnd.eszigno3+xml es3 ES3
application/vnd.osgi.subsystem esa ESA
application/vnd.epson.esf esf ESF
application/vnd.eszigno3+xml et3 ET3
text/x-setext etx ETX
application/x-eva eva EVA
application/x-envoy evy EVY
application/octet-stream exe EXE
application/exi exi EXI
application/vnd.novadigm.ext ext EXT
application/andrew-inset ez EZ
application/vnd.ezpix-album ez2 EZ2
application/vnd.ezpix-package ez3 EZ3
text/x-fortran f F
video/x-f4v f4v F4V
text/x-fortran f77 F77
text/x-fortran f90 F90
image/vnd.fastbidsheet fbs FBS
application/vnd.adobe.formscentral.fcdt fcdt FCDT
application/vnd.isac.fcs fcs FCS
application/vnd.fdf fdf FDF
application/vnd.denovo.fcselayout-link fe_launch FE_LAUNCH
application/vnd.fujitsu.oasysgp fg5 FG5
application/x-director fgd FGD
image/x-freehand fh FH
image/x-freehand fh4 FH4
image/x-freehand fh5 FH5
image/x-freehand fh7 FH7
image/x-freehand fhc FHC
application/x-xfig fig FIG
audio/flac flac FLAC
video/x-fli fli FLI
application/vnd.micrografx.flo flo FLO
video/x-flv flv FLV
application/vnd.kde.kivio flw FLW
text/vnd.fmi.flexstor flx FLX
text/vnd.fly fly FLY
application/vnd.framemaker fm FM
application/vnd.frogans.fnc fnc FNC
text/x-fortran for FOR
image/vnd.fpx fpx FPX
application/vnd.framemaker frame FRAME
application/vnd.fsc.weblaunch fsc FSC
image/vnd.fst fst FST
application/vnd.fluxtime.clip ftc FTC
application/vnd.anser-web-funds-transfer-initiation fti FTI
video/vnd.fvt fvt FVT
application/vnd.adobe.fxp fxp FXP
application/vnd.adobe.fxp fxpl FXPL
application/vnd.fuzzysheet fzs FZS
application/vnd.geoplan g2w G2W
image/g3fax g3 G3
application/vnd.geospace g3w G3W
application/vnd.groove-account gac GAC
application/x-tads gam GAM
application/rpki-ghostbusters gbr GBR
application/x-gca-compressed gca GCA
model/vnd.gdl gdl GDL
application/vnd.dynageo geo GEO
application/vnd.geometry-explorer gex GEX
application/vnd.geogebra.file ggb GGB
application/vnd.geogebra.tool ggt GGT
application/vnd.groove-help ghf GHF
image/gif gif GIF
application/vnd.groove-identity-message gim GIM
application/gml+xml gml GML
application/vnd.gmx gmx GMX
application/x-gnumeric gnumeric GNUMERIC
application/vnd.flographit gph GPH
application/gpx+xml gpx GPX
application/vnd.grafeq gqf GQF
application/vnd.grafeq gqs GQS
application/srgs gram GRAM
application/x-gramps-xml gramps GRAMPS
application/vnd.geometry-explorer gre GRE
application/vnd.groove-injector grv GRV
application/srgs+xml grxml GRXML
application/x-font-ghostscript gsf GSF
application/x-gtar gtar GTAR
application/vnd.groove-tool-message gtm GTM
model/vnd.gtw gtw GTW
text/vnd.graphviz gv GV
application/gxf gxf GXF
application/vnd.geonext gxt GXT
application/x-gzip gz GZ
text/x-c h H
video/h261 h261 H261
video/h263 h263 H263
video/h264 h264 H264
application/vnd.hal+xml hal HAL
application/vnd.hbci hbci HBCI
application/x-hdf hdf HDF
text/x-c hh HH
application/winhlp hlp HLP
application/vnd.hp-hpgl hpgl HPGL
application/vnd.hp-hpid hpid HPID
application/vnd.hp-hps hps HPS
application/mac-binhex40 hqx HQX
text/x-component htc HTC
application/vnd.kenameaapp htke HTKE
text/html htm HTM
text/html html HTML
application/vnd.yamaha.hv-dic hvd HVD
application/vnd.yamaha.hv-voice hvp HVP
application/vnd.yamaha.hv-script hvs HVS
application/vnd.intergeo i2g I2G
application/vnd.iccprofile icc ICC
x-conference/x-cooltalk ice ICE
application/vnd.iccprofile icm ICM
image/x-icon ico ICO
text/calendar ics ICS
image/ief ief IEF
text/calendar ifb IFB
application/vnd.shana.informed.formdata ifm IFM
model/iges iges IGES
application/vnd.igloader igl IGL
application/vnd.insors.igm igm IGM
model/iges igs IGS
application/vnd.micrografx.igx igx IGX
application/vnd.shana.informed.interchange iif IIF
application/vnd.accpac.simply.imp imp IMP
application/vnd.ms-ims ims IMS
text/plain in IN
application/inkml+xml ink INK
application/inkml+xml inkml INKML
application/x-install-instructions install INSTALL
application/vnd.astraea-software.iota iota IOTA
application/ipfix ipfix IPFIX
application/vnd.shana.informed.package ipk IPK
application/vnd.ibm.rights-management irm IRM
application/vnd.irepository.package+xml irp IRP
application/x-iso9660-image iso ISO
application/vnd.shana.informed.formtemplate itp ITP
application/vnd.immervision-ivp ivp IVP
application/vnd.immervision-ivu ivu IVU
text/vnd.sun.j2me.app-descriptor jad JAD
application/vnd.jam jam JAM
application/java-archive jar JAR
text/x-java-source java JAVA
application/vnd.jisp jisp JISP
application/vnd.hp-jlyt jlt JLT
application/x-java-jnlp-file jnlp JNLP
application/vnd.joost.joda-archive joda JODA
image/jpeg jpe JPE
image/jpeg jpeg JPEG
image/jpeg jpg JPG
video/jpm jpgm JPGM
video/jpeg jpgv JPGV
video/jpm jpm JPM
application/javascript js JS
text/plain jsf JSF
application/json json JSON
application/jsonml+json jsonml JSONML
text/plain jspf JSPF
audio/midi kar KAR
application/vnd.kde.karbon karbon KARBON
application/vnd.kde.kformula kfo KFO
application/vnd.kidspiration kia KIA
application/vnd.google-earth.kml+xml kml KML
application/vnd.google-earth.kmz kmz KMZ
application/vnd.kinar kne KNE
application/vnd.kinar knp KNP
application/vnd.kde.kontour kon KON
application/vnd.kde.kpresenter kpr KPR
application/vnd.kde.kpresenter kpt KPT
application/vnd.ds-keypoint kpxx KPXX
application/vnd.kde.kspread ksp KSP
application/vnd.kahootz ktr KTR
image/ktx ktx KTX
application/vnd.kahootz ktz KTZ
application/vnd.kde.kword kwd KWD
application/vnd.kde.kword kwt KWT
application/vnd.las.las+xml lasxml LASXML
application/x-latex latex LATEX
application/vnd.llamagraphics.life-balance.desktop lbd LBD
application/vnd.llamagraphics.life-balance.exchange+xml lbe LBE
application/vnd.hhe.lesson-player les LES
application/x-lzh-compressed lha LHA
application/vnd.route66.link66+xml link66 LINK66
text/plain list LIST
application/vnd.ibm.modcap list3820 LIST3820
application/vnd.ibm.modcap listafp LISTAFP
application/x-ms-shortcut lnk LNK
text/plain log LOG
application/lost+xml lostxml LOSTXML
application/octet-stream lrf LRF
application/vnd.ms-lrm lrm LRM
application/vnd.frogans.ltf ltf LTF
audio/vnd.lucent.voice lvp LVP
application/vnd.lotus-wordpro lwp LWP
application/x-lzh-compressed lzh LZH
application/x-msmediaview m13 M13
application/x-msmediaview m14 M14
video/mpeg m1v M1V
application/mp21 m21 M21
audio/mpeg m2a M2A
video/mpeg m2v M2V
audio/mpeg m3a M3A
audio/x-mpegurl m3u M3U
application/vnd.apple.mpegurl m3u8 M3U8
audio/mp4 m4a M4A
audio/mp4 m4b M4B
audio/mp4 m4r M4R
video/vnd.mpegurl m4u M4U
video/mp4 m4v M4V
application/mathematica ma MA
image/x-macpaint mac MAC
application/mads+xml mads MADS
application/vnd.ecowin.chart mag MAG
application/vnd.framemaker maker MAKER
text/troff man MAN
application/octet-stream mar MAR
application/mathml+xml mathml MATHML
application/mathematica mb MB
application/vnd.mobius.mbk mbk MBK
application/mbox mbox MBOX
application/vnd.medcalcdata mc1 MC1
application/vnd.mcd mcd MCD
text/vnd.curl.mcurl mcurl MCURL
application/x-msaccess mdb MDB
image/vnd.ms-modi mdi MDI
text/troff me ME
model/mesh mesh MESH
application/metalink4+xml meta4 META4
application/metalink+xml metalink METALINK
application/mets+xml mets METS
application/vnd.mfmp mfm MFM
application/rpki-manifest mft MFT
application/vnd.osgeo.mapguide.package mgp MGP
application/vnd.proteus.magazine mgz MGZ
audio/midi mid MID
audio/midi midi MIDI
application/x-mie mie MIE
application/x-mif mif MIF
message/rfc822 mime MIME
video/mj2 mj2 MJ2
video/mj2 mjp2 MJP2
video/x-matroska mk3d MK3D
audio/x-matroska mka MKA
video/x-matroska mks MKS
video/x-matroska mkv MKV
application/vnd.dolby.mlp mlp MLP
application/vnd.chipnuts.karaoke-mmd mmd MMD
application/vnd.smaf mmf MMF
image/vnd.fujixerox.edmics-mmr mmr MMR
video/x-mng mng MNG
application/x-msmoney mny MNY
application/x-mobipocket-ebook mobi MOBI
application/mods+xml mods MODS
video/quicktime mov MOV
video/x-sgi-movie movie MOVIE
audio/mpeg mp1 MP1
audio/mpeg mp2 MP2
application/mp21 mp21 MP21
audio/mpeg mp2a MP2A
audio/mpeg mp3 MP3
video/mp4 mp4 MP4
audio/mp4 mp4a MP4A
application/mp4 mp4s MP4S
video/mp4 mp4v MP4V
audio/mpeg mpa MPA
application/vnd.mophun.certificate mpc MPC
video/mpeg mpe MPE
video/mpeg mpeg MPEG
audio/x-mpeg mpega MPEGA
video/mpeg mpg MPG
video/mp4 mpg4 MPG4
audio/mpeg mpga MPGA
application/vnd.apple.installer+xml mpkg MPKG
application/vnd.blueice.multipass mpm MPM
application/vnd.mophun.application mpn MPN
application/vnd.ms-project mpp MPP
application/vnd.ms-project mpt MPT
video/mpeg2 mpv2 MPV2
application/vnd.ibm.minipay mpy MPY
application/vnd.mobius.mqy mqy MQY
application/marc mrc MRC
application/marcxml+xml mrcx MRCX
text/troff ms MS
application/mediaservercontrol+xml mscml MSCML
application/vnd.fdsn.mseed mseed MSEED
application/vnd.mseq mseq MSEQ
application/vnd.epson.msf msf MSF
model/mesh msh MSH
application/x-msdownload msi MSI
application/vnd.mobius.msl msl MSL
application/vnd.muvee.style msty MSTY
model/vnd.mts mts MTS
application/vnd.musician mus MUS
application/vnd.recordare.musicxml+xml musicxml MUSICXML
application/x-msmediaview mvb MVB
application/vnd.mfer mwf MWF
application/mxf mxf MXF
application/vnd.recordare.musicxml mxl MXL
application/xv+xml mxml MXML
application/vnd.triscape.mxs mxs MXS
video/vnd.mpegurl mxu MXU
application/vnd.nokia.n-gage.symbian.install n-gage N-GAGE
text/n3 n3 N3
application/mathematica nb NB
application/vnd.wolfram.player nbp NBP
application/x-netcdf nc NC
application/x-dtbncx+xml ncx NCX
text/x-nfo nfo NFO
application/vnd.nokia.n-gage.data ngdat NGDAT
application/vnd.nitf nitf NITF
application/vnd.neurolanguage.nlu nlu NLU
application/vnd.enliven nml NML
application/vnd.noblenet-directory nnd NND
application/vnd.noblenet-sealer nns NNS
application/vnd.noblenet-web nnw NNW
image/vnd.net-fpx npx NPX
application/x-conference nsc NSC
application/vnd.lotus-notes nsf NSF
application/vnd.nitf ntf NTF
application/x-nzb nzb NZB
application/vnd.fujitsu.oasys2 oa2 OA2
application/vnd.fujitsu.oasys3 oa3 OA3
application/vnd.fujitsu.oasys oas OAS
application/x-msbinder obd OBD
application/x-tgif obj OBJ
application/oda oda ODA
application/vnd.oasis.opendocument.database odb ODB
application/vnd.oasis.opendocument.chart odc ODC
application/vnd.oasis.opendocument.formula odf ODF
application/vnd.oasis.opendocument.formula-template odft ODFT
application/vnd.oasis.opendocument.graphics odg ODG
application/vnd.oasis.opendocument.image odi ODI
application/vnd.oasis.opendocument.text-master odm ODM
application/vnd.oasis.opendocument.presentation odp ODP
application/vnd.oasis.opendocument.spreadsheet ods ODS
application/vnd.oasis.opendocument.text odt ODT
audio/ogg oga OGA
audio/ogg ogg OGG
video/ogg ogv OGV
application/ogg ogx OGX
application/omdoc+xml omdoc OMDOC
application/onenote onepkg ONEPKG
application/onenote onetmp ONETMP
application/onenote onetoc ONETOC
application/onenote onetoc2 ONETOC2
application/oebps-package+xml opf OPF
text/x-opml opml OPML
application/vnd.palm oprc OPRC
application/vnd.lotus-organizer org ORG
application/vnd.yamaha.openscoreformat osf OSF
application/vnd.yamaha.openscoreformat.osfpvg+xml osfpvg OSFPVG
application/vnd.oasis.opendocument.chart-template otc OTC
font/otf otf OTF
application/vnd.oasis.opendocument.graphics-template otg OTG
application/vnd.oasis.opendocument.text-web oth OTH
application/vnd.oasis.opendocument.image-template oti OTI
application/vnd.oasis.opendocument.presentation-template otp OTP
application/vnd.oasis.opendocument.spreadsheet-template ots OTS
application/vnd.oasis.opendocument.text-template ott OTT
application/oxps oxps OXPS
application/vnd.openofficeorg.extension oxt OXT
text/x-pascal p P
application/pkcs10 p10 P10
application/x-pkcs12 p12 P12
application/x-pkcs7-certificates p7b P7B
application/pkcs7-mime p7c P7C
application/pkcs7-mime p7m P7M
application/x-pkcs7-certreqresp p7r P7R
application/pkcs7-signature p7s P7S
application/pkcs8 p8 P8
text/x-pascal pas PAS
application/vnd.pawaafile paw PAW
application/vnd.powerbuilder6 pbd PBD
image/x-portable-bitmap pbm PBM
application/vnd.tcpdump.pcap pcap PCAP
application/x-font-pcf pcf PCF
application/vnd.hp-pcl pcl PCL
application/vnd.hp-pclxl pclxl PCLXL
image/pict pct PCT
application/vnd.curl.pcurl pcurl PCURL
image/x-pcx pcx PCX
application/vnd.palm pdb PDB
application/pdf pdf PDF
application/x-font-type1 pfa PFA
application/x-font-type1 pfb PFB
application/x-font-type1 pfm PFM
application/font-tdpfr pfr PFR
application/x-pkcs12 pfx PFX
image/x-portable-graymap pgm PGM
application/x-chess-pgn pgn PGN
application/pgp-encrypted pgp PGP
image/pict pic PIC
image/pict pict PICT
application/octet-stream pkg PKG
application/pkixcmp pki PKI
application/pkix-pkipath pkipath PKIPATH
application/vnd.3gpp.pic-bw-large plb PLB
application/vnd.mobius.plc plc PLC
application/vnd.pocketlearn plf PLF
audio/x-scpls pls PLS
application/vnd.ctc-posml pml PML
image/png png PNG
image/x-portable-anymap pnm PNM
image/x-macpaint pnt PNT
application/vnd.macports.portpkg portpkg PORTPKG
application/vnd.ms-powerpoint pot POT
application/vnd.ms-powerpoint.template.macroenabled.12 potm POTM
application/vnd.openxmlformats-officedocument.presentationml.template potx POTX
application/vnd.ms-powerpoint.addin.macroenabled.12 ppam PPAM
application/vnd.cups-ppd ppd PPD
image/x-portable-pixmap ppm PPM
application/vnd.ms-powerpoint pps PPS
application/vnd.ms-powerpoint.slideshow.macroenabled.12 ppsm PPSM
application/vnd.openxmlformats-officedocument.presentationml.slideshow ppsx PPSX
application/vnd.ms-powerpoint ppt PPT
application/vnd.ms-powerpoint.presentation.macroenabled.12 pptm PPTM
application/vnd.openxmlformats-officedocument.presentationml.presentation pptx PPTX
application/vnd.palm pqa PQA
application/x-mobipocket-ebook prc PRC
application/vnd.lotus-freelance pre PRE
application/pics-rules prf PRF
application/postscript ps PS
application/vnd.3gpp.pic-bw-small psb PSB
image/vnd.adobe.photoshop psd PSD
application/x-font-linux-psf psf PSF
application/pskc+xml pskcxml PSKCXML
application/vnd.pvi.ptid1 ptid PTID
application/x-mspublisher pub PUB
application/vnd.3gpp.pic-bw-var pvb PVB
application/vnd.3m.post-it-notes pwn PWN
audio/vnd.ms-playready.media.pya pya PYA
video/vnd.ms-playready.media.pyv pyv PYV
application/vnd.epson.quickanime qam QAM
application/vnd.intu.qbo qbo QBO
application/vnd.intu.qfx qfx QFX
application/vnd.publishare-delta-tree qps QPS
video/quicktime qt QT
image/x-quicktime qti QTI
image/x-quicktime qtif QTIF
application/vnd.quark.quarkxpress qwd QWD
application/vnd.quark.quarkxpress qwt QWT
application/vnd.quark.quarkxpress qxb QXB
application/vnd.quark.quarkxpress qxd QXD
application/vnd.quark.quarkxpress qxl QXL
application/vnd.quark.quarkxpress qxt QXT
audio/x-pn-realaudio ra RA
audio/x-pn-realaudio ram RAM
application/x-rar-compressed rar RAR
image/x-cmu-raster ras RAS
application/vnd.ipunplugged.rcprofile rcprofile RCPROFILE
application/rdf+xml rdf RDF
application/vnd.data-vision.rdz rdz RDZ
application/vnd.businessobjects rep REP
application/x-dtbresource+xml res RES
image/x-rgb rgb RGB
application/reginfo+xml rif RIF
audio/vnd.rip rip RIP
application/x-research-info-systems ris RIS
application/resource-lists+xml rl RL
image/vnd.fujixerox.edmics-rlc rlc RLC
application/resource-lists-diff+xml rld RLD
application/vnd.rn-realmedia rm RM
audio/midi rmi RMI
audio/x-pn-realaudio-plugin rmp RMP
application/vnd.jcp.javame.midlet-rms rms RMS
application/vnd.rn-realmedia-vbr rmvb RMVB
application/relax-ng-compact-syntax rnc RNC
application/rpki-roa roa ROA
text/troff roff ROFF
application/vnd.cloanto.rp9 rp9 RP9
application/vnd.nokia.radio-presets rpss RPSS
application/vnd.nokia.radio-preset rpst RPST
application/sparql-query rq RQ
application/rls-services+xml rs RS
application/rsd+xml rsd RSD
application/rss+xml rss RSS
application/rtf rtf RTF
text/richtext rtx RTX
text/x-asm s S
audio/s3m s3m S3M
application/vnd.yamaha.smaf-audio saf SAF
application/sbml+xml sbml SBML
application/vnd.ibm.secure-container sc SC
application/x-msschedule scd SCD
application/vnd.lotus-screencam scm SCM
application/scvp-cv-request scq SCQ
application/scvp-cv-response scs SCS
text/vnd.curl.scurl scurl SCURL
application/vnd.stardivision.draw sda SDA
application/vnd.stardivision.calc sdc SDC
application/vnd.stardivision.impress sdd SDD
application/vnd.solent.sdkm+xml sdkd SDKD
application/vnd.solent.sdkm+xml sdkm SDKM
application/sdp sdp SDP
application/vnd.stardivision.writer sdw SDW
application/vnd.seemail see SEE
application/vnd.fdsn.seed seed SEED
application/vnd.sema sema SEMA
application/vnd.semd semd SEMD
application/vnd.semf semf SEMF
application/java-serialized-object ser SER
application/set-payment-initiation setpay SETPAY
application/set-registration-initiation setreg SETREG
application/vnd.hydrostatix.sof-data sfd-hdstx SFD-HDSTX
application/vnd.spotfire.sfs sfs SFS
text/x-sfv sfv SFV
image/sgi sgi SGI
application/vnd.stardivision.writer-global sgl SGL
text/sgml sgm SGM
text/sgml sgml SGML
application/x-sh sh SH
application/x-shar shar SHAR
application/shf+xml shf SHF
image/x-mrsid-image sid SID
application/pgp-signature sig SIG
audio/silk sil SIL
model/mesh silo SILO
application/vnd.symbian.install sis SIS
application/vnd.symbian.install sisx SISX
application/x-stuffit sit SIT
application/x-stuffitx sitx SITX
application/vnd.koan skd SKD
application/vnd.koan skm SKM
application/vnd.koan skp SKP
application/vnd.koan skt SKT
application/vnd.ms-powerpoint.slide.macroenabled.12 sldm SLDM
application/vnd.openxmlformats-officedocument.presentationml.slide sldx SLDX
application/vnd.epson.salt slt SLT
application/vnd.stepmania.stepchart sm SM
application/vnd.stardivision.math smf SMF
application/smil+xml smi SMI
application/smil+xml smil SMIL
video/x-smv smv SMV
application/vnd.stepmania.package smzip SMZIP
audio/basic snd SND
application/x-font-snf snf SNF
application/octet-stream so SO
application/x-pkcs7-certificates spc SPC
application/vnd.yamaha.smaf-phrase spf SPF
application/x-futuresplash spl SPL
text/vnd.in3d.spot spot SPOT
application/scvp-vp-response spp SPP
application/scvp-vp-request spq SPQ
audio/ogg spx SPX
application/x-sql sql SQL
application/x-wais-source src SRC
application/x-subrip srt SRT
application/sru+xml sru SRU
application/sparql-results+xml srx SRX
application/ssdl+xml ssdl SSDL
application/vnd.kodak-descriptor sse SSE
application/vnd.epson.ssf ssf SSF
application/ssml+xml ssml SSML
application/vnd.sailingtracker.track st ST
application/vnd.sun.xml.calc.template stc STC
application/vnd.sun.xml.draw.template std STD
application/vnd.wt.stf stf STF
application/vnd.sun.xml.impress.template sti STI
application/hyperstudio stk STK
application/vnd.ms-pki.stl stl STL
application/vnd.pg.format str STR
application/vnd.sun.xml.writer.template stw STW
text/vnd.dvb.subtitle sub SUB
application/vnd.sus-calendar sus SUS
application/vnd.sus-calendar susp SUSP
application/x-sv4cpio sv4cpio SV4CPIO
application/x-sv4crc sv4crc SV4CRC
application/vnd.dvb.service svc SVC
application/vnd.svd svd SVD
image/svg+xml svg SVG
image/svg+xml svgz SVGZ
application/x-director swa SWA
application/x-shockwave-flash swf SWF
application/vnd.aristanetworks.swi swi SWI
application/vnd.sun.xml.calc sxc SXC
application/vnd.sun.xml.draw sxd SXD
application/vnd.sun.xml.writer.global sxg SXG
application/vnd.sun.xml.impress sxi SXI
application/vnd.sun.xml.math sxm SXM
application/vnd.sun.xml.writer sxw SXW
text/troff t T
application/x-t3vm-image t3 T3
application/vnd.mynfc taglet TAGLET
application/vnd.tao.intent-module-archive tao TAO
application/x-tar tar TAR
application/vnd.3gpp2.tcap tcap TCAP
application/x-tcl tcl TCL
application/vnd.smart.teacher teacher TEACHER
application/tei+xml tei TEI
application/tei+xml teicorpus TEICORPUS
application/x-tex tex TEX
application/x-texinfo texi TEXI
application/x-texinfo texinfo TEXINFO
text/plain text TEXT
application/thraud+xml tfi TFI
application/x-tex-tfm tfm TFM
image/x-tga tga TGA
application/vnd.ms-officetheme thmx THMX
image/tiff tif TIF
image/tiff tiff TIFF
application/vnd.tmobile-livetv tmo TMO
application/x-bittorrent torrent TORRENT
application/vnd.groove-tool-template tpl TPL
application/vnd.trid.tpt tpt TPT
text/troff tr TR
application/vnd.trueapp tra TRA
application/x-msterminal trm TRM
application/timestamped-data tsd TSD
text/tab-separated-values tsv TSV
font/collection ttc TTC
font/ttf ttf TTF
text/turtle ttl TTL
application/vnd.simtech-mindmapper twd TWD
application/vnd.simtech-mindmapper twds TWDS
application/vnd.genomatix.tuxedo txd TXD
application/vnd.mobius.txf txf TXF
text/plain txt TXT
application/x-authorware-bin u32 U32
application/x-debian-package udeb UDEB
application/vnd.ufdl ufd UFD
application/vnd.ufdl ufdl UFDL
audio/basic ulw ULW
application/x-glulx ulx ULX
application/vnd.umajin umj UMJ
application/vnd.unity unityweb UNITYWEB
application/vnd.uoml+xml uoml UOML
text/uri-list uri URI
text/uri-list uris URIS
text/uri-list urls URLS
application/x-ustar ustar USTAR
application/vnd.uiq.theme utz UTZ
text/x-uuencode uu UU
audio/vnd.dece.audio uva UVA
application/vnd.dece.data uvd UVD
application/vnd.dece.data uvf UVF
image/vnd.dece.graphic uvg UVG
video/vnd.dece.hd uvh UVH
image/vnd.dece.graphic uvi UVI
video/vnd.dece.mobile uvm UVM
video/vnd.dece.pd uvp UVP
video/vnd.dece.sd uvs UVS
application/vnd.dece.ttml+xml uvt UVT
video/vnd.uvvu.mp4 uvu UVU
video/vnd.dece.video uvv UVV
audio/vnd.dece.audio uvva UVVA
application/vnd.dece.data uvvd UVVD
application/vnd.dece.data uvvf UVVF
image/vnd.dece.graphic uvvg UVVG
video/vnd.dece.hd uvvh UVVH
image/vnd.dece.graphic uvvi UVVI
video/vnd.dece.mobile uvvm UVVM
video/vnd.dece.pd uvvp UVVP
video/vnd.dece.sd uvvs UVVS
application/vnd.dece.ttml+xml uvvt UVVT
video/vnd.uvvu.mp4 uvvu UVVU
video/vnd.dece.video uvvv UVVV
application/vnd.dece.unspecified uvvx UVVX
application/vnd.dece.zip uvvz UVVZ
application/vnd.dece.unspecified uvx UVX
application/vnd.dece.zip uvz UVZ
text/vcard vcard VCARD
application/x-cdlink vcd VCD
text/x-vcard vcf VCF
application/vnd.groove-vcard vcg VCG
text/x-vcalendar vcs VCS
application/vnd.vcx vcx VCX
application/vnd.visionary vis VIS
video/vnd.vivo viv VIV
video/x-ms-vob vob VOB
application/vnd.stardivision.writer vor VOR
application/x-authorware-bin vox VOX
model/vrml vrml VRML
application/vnd.visio vsd VSD
application/vnd.vsf vsf VSF
application/vnd.visio vss VSS
application/vnd.visio vst VST
application/vnd.visio vsw VSW
model/vnd.vtu vtu VTU
application/voicexml+xml vxml VXML
application/x-director w3d W3D
application/x-doom wad WAD
audio/x-wav wav WAV
audio/x-ms-wax wax WAX
image/vnd.wap.wbmp wbmp WBMP
application/vnd.criticaltools.wbs+xml wbs WBS
application/vnd.wap.wbxml wbxml WBXML
application/vnd.ms-works wcm WCM
application/vnd.ms-works wdb WDB
image/vnd.ms-photo wdp WDP
audio/webm weba WEBA
video/webm webm WEBM
image/webp webp WEBP
application/vnd.pmi.widget wg WG
application/widget wgt WGT
application/vnd.ms-works wks WKS
video/x-ms-wm wm WM
audio/x-ms-wma wma WMA
application/x-ms-wmd wmd WMD
application/x-msmetafile wmf WMF
text/vnd.wap.wml wml WML
application/vnd.wap.wmlc wmlc WMLC
text/vnd.wap.wmlscript wmls WMLS
application/vnd.wap.wmlscriptc wmlsc WMLSC
video/x-ms-wmv wmv WMV
video/x-ms-wmx wmx WMX
application/x-msmetafile wmz WMZ
font/woff woff WOFF
font/woff2 woff2 WOFF2
application/vnd.wordperfect wpd WPD
application/vnd.ms-wpl wpl WPL
application/vnd.ms-works wps WPS
application/vnd.wqd wqd WQD
application/x-mswrite wri WRI
model/vrml wrl WRL
application/wsdl+xml wsdl WSDL
application/wspolicy+xml wspolicy WSPOLICY
application/vnd.webturbo wtb WTB
video/x-ms-wvx wvx WVX
application/x-authorware-bin x32 X32
model/x3d+xml x3d X3D
model/x3d+binary x3db X3DB
model/x3d+binary x3dbz X3DBZ
model/x3d+vrml x3dv X3DV
model/x3d+vrml x3dvz X3DVZ
model/x3d+xml x3dz X3DZ
application/xaml+xml xaml XAML
application/x-silverlight-app xap XAP
application/vnd.xara xar XAR
application/x-ms-xbap xbap XBAP
application/vnd.fujixerox.docuworks.binder xbd XBD
image/x-xbitmap xbm XBM
application/xcap-diff+xml xdf XDF
application/vnd.syncml.dm+xml xdm XDM
application/vnd.adobe.xdp+xml xdp XDP
application/dssc+xml xdssc XDSSC
application/vnd.fujixerox.docuworks xdw XDW
application/xenc+xml xenc XENC
application/patch-ops-error+xml xer XER
application/vnd.adobe.xfdf xfdf XFDF
application/vnd.xfdl xfdl XFDL
application/xhtml+xml xht XHT
application/xhtml+xml xhtml XHTML
application/xv+xml xhvml XHVML
image/vnd.xiff xif XIF
application/vnd.ms-excel xla XLA
application/vnd.ms-excel.addin.macroenabled.12 xlam XLAM
application/vnd.ms-excel xlc XLC
application/x-xliff+xml xlf XLF
application/vnd.ms-excel xlm XLM
application/vnd.ms-excel xls XLS
application/vnd.ms-excel.sheet.binary.macroenabled.12 xlsb XLSB
application/vnd.ms-excel.sheet.macroenabled.12 xlsm XLSM
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx XLSX
application/vnd.ms-excel xlt XLT
application/vnd.ms-excel.template.macroenabled.12 xltm XLTM
application/vnd.openxmlformats-officedocument.spreadsheetml.template xltx XLTX
application/vnd.ms-excel xlw XLW
audio/xm xm XM
application/xml xml XML
application/vnd.olpc-sugar xo XO
application/xop+xml xop XOP
application/x-xpinstall xpi XPI
application/xproc+xml xpl XPL
image/x-xpixmap xpm XPM
application/vnd.is-xpr xpr XPR
application/vnd.ms-xpsdocument xps XPS
application/vnd.intercon.formnet xpw XPW
application/vnd.intercon.formnet xpx XPX
application/xml xsl XSL
application/xslt+xml xslt XSLT
application/vnd.syncml+xml xsm XSM
application/xspf+xml xspf XSPF
application/vnd.mozilla.xul+xml xul XUL
application/xv+xml xvm XVM
application/xv+xml xvml XVML
image/x-xwindowdump xwd XWD
chemical/x-xyz xyz XYZ
application/x-xz xz XZ
application/yang yang YANG
application/yin+xml yin YIN
application/x-compress z Z
application/x-compress Z Z
application/x-zmachine z1 Z1
application/x-zmachine z2 Z2
application/x-zmachine z3 Z3
application/x-zmachine z4 Z4
application/x-zmachine z5 Z5
application/x-zmachine z6 Z6
application/x-zmachine z7 Z7
application/x-zmachine z8 Z8
application/vnd.zzazz.deck+xml zaz ZAZ
application/zip zip ZIP
application/vnd.zul zir ZIR
application/vnd.zul zirz ZIRZ
application/vnd.handheld-entertainment+xml zmm ZMM
解决空请求问题
HTTP协议注明:为了保证服务端的健壮性,应当忽略客户端空的请求。
浏览器有时会发送空请求,即:与服务端链接后没有发送标准的HTTP请求内容,直接与服务端断开链接。
此时服务端按照一问一答的处理流程在解析请求时请求行由于没有内容,在拆分后获取信息会出现数组下标越界。
解决:当解析请求行时发现没有内容就对外抛出空请求异常(自定义的一个异常),并最终抛出给ClientHandler
,使其忽略后续的处理请求与发送响应的工作,直接与客户端断开来忽略本次操作。实现:
- 在
com.webserver.http
包下新建自定义异常EmptyRequestException
,空请求异常- 在
HttpServletRequest
的解析请求行方法parseRequestLine中
,当读取请求行后发现是一个空字符串则对外抛出空请求异常并通过构造方法继续对外抛出给ClientHandler
ClientHandler
添加一个新的catch
专门捕获空请求异常,捕获后无需做任何处理,目的仅仅是忽略处理请求和响应客户端的工作
package com.birdboot.http;
/**
* 空请求异常
* 当HttpServletRequest解析请求时发现本次为空请求就会抛出该异常
* 空请求现象:浏览器连接服务端后仅发送回车换行,而没有发送HTTP协议要求的请求内容。
*/
public class EmptyRequestException extends Exception{
public EmptyRequestException() {
}
public EmptyRequestException(String message) {
super(message);
}
public EmptyRequestException(String message, Throwable cause) {
super(message, cause);
}
public EmptyRequestException(Throwable cause) {
super(cause);
}
public EmptyRequestException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
package com.birdboot.core;
import com.birdboot.http.EmptyRequestException;
import com.birdboot.http.HttpServletRequest;
import com.birdboot.http.HttpServletResponse;
import java.io.*;
import java.net.Socket;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* 该线程任务负责与指定的客户端进行HTTP交互
* HTTP协议要求浏览器与服务端采取"一问一答"的模式。对此,这里的处理流程分为三步:
* 1:解析请求
* 2:处理请求
* 3:发送响应
*/
public class ClientHandler implements Runnable {
private Socket socket;
public ClientHandler(Socket socket) {
this.socket = socket;
}
public void run() {
try {
//1 解析请求
HttpServletRequest request = new HttpServletRequest(socket);
HttpServletResponse response = new HttpServletResponse(socket);
//2 处理请求
//V8改造:将处理请求的操作移动到DispatcherServlet的service方法中并调用
DispatcherServlet servlet = new DispatcherServlet();
servlet.service(request,response);
//3 发送响应
response.response();
} catch (IOException e) {
e.printStackTrace();
} catch (EmptyRequestException e) {
} finally {
//HTTP协议要求浏览器与服务端交互完毕后要断开连接
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.birdboot.http;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
/**
* V4:新增内容
* 请求对象
* 该类的每一个实例用于表示浏览器发送过来的一个HTTP请求
* HTTP协议要求请求的格式由三部分构成:请求行,消息头,消息正文
*/
public class HttpServletRequest {
private Socket socket;
//请求行相关信息
private String method;//请求方式
private String uri;//抽象路径
private String protocol;//协议版本
//消息头相关信息 key:消息头名字 value:消息头对应的值
private Map<String,String> headers = new HashMap<>();
public HttpServletRequest(Socket socket) throws IOException, EmptyRequestException {
this.socket = socket;
//1.1解析请求行
parseRequestLine();
//1.2解析消息头
parseHeaders();
//1.3解析消息正文
parseContent();
}
//解析请求行
private void parseRequestLine() throws IOException, EmptyRequestException {
String line = readLine();
if(line.isEmpty()){//如果请求行是个空字符串,则说明本次为空请求
throw new EmptyRequestException();
}
System.out.println("请求行:"+line);
//将请求行按照空格("\s"在正则表达式中表示一个空白字符,包含空格)拆分为三部分
String[] data = line.split("\\s");
method = data[0];
uri = data[1];
protocol = data[2];
System.out.println("method:"+method);
System.out.println("uri:"+uri);
System.out.println("protocol:"+protocol);
}
//解析消息头
private void parseHeaders() throws IOException {
while(true) {
String line = readLine();
if(line.isEmpty()){//如果读取到了空行
break;
}
System.out.println("消息头:" + line);
String[] data = line.split(":\\s");
headers.put(data[0],data[1]);
}
System.out.println("headers:"+headers);
}
//解析消息正文
private void parseContent(){}
/**
* 通过socket获取的输入流读取客户端发送过来的一行字符串
* @return
*/
private String readLine() throws IOException {//通常被重用的代码不自己处理异常
//对一个socket实例调用多次getInputStream()返回的始终是同一条输入流。而输出流也是如此
InputStream in = socket.getInputStream();
int d;
char pre='a',cur='a';//pre表示上次读取的字符,cur表示本次读取的字符
StringBuilder builder = new StringBuilder();//保存读取后的所有字符
while((d = in.read())!=-1){
cur = (char)d;//本次读取的字符
if(pre==13 && cur==10){//是否连续读取到了回车+换行
break;
}
builder.append(cur);//将本次读取的字符拼接
pre=cur;//在进行下次读取前,将本次读取的字符保存到"上次读取的字符"中
}
return builder.toString().trim();
}
public String getMethod() {
return method;
}
public String getUri() {
return uri;
}
public String getProtocol() {
return protocol;
}
/**
* 根据给定的消息头的名字获取对应消息头的值
* @param name
* @return
*/
public String getHeader(String name) {
return headers.get(name);
}
}
package com.birdboot.http;
import javax.activation.MimetypesFileTypeMap;
import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* V7新增内容:
* 响应对象
* 该类的每一个实例用于表示服务端给客户端发送的一个HTTP的响应
* HTTP协议要求一个响应由三部分构成:状态行,响应头,响应正文
*/
public class HttpServletResponse {
private static MimetypesFileTypeMap mftm = new MimetypesFileTypeMap();
private Socket socket;
//状态行相关信息
private int statusCode = 200;//状态代码
private String statusReason = "OK";//状态描述
//响应头相关信息 key:响应头的名字 value:响应头的值
private Map<String,String> headers = new HashMap<>();
//响应正文相关信息
private File contentFile;//响应正文对应的实体文件
public HttpServletResponse(Socket socket){
this.socket = socket;
}
/**
* 该方法用于将当前响应对象内容以标准的HTTP响应格式发送给客户端
*/
public void response() throws IOException {
//3.1发送状态行
sendStatusLine();
//3.2发送响应头
sendHeaders();
//3.3发送响应正文
sendContent();
}
//发送状态行
private void sendStatusLine() throws IOException {
println("HTTP/1.1"+" "+statusCode+" "+statusReason);
}
//发送响应头
private void sendHeaders() throws IOException {
/*
遍历headers将所有待发送的响应头发送给浏览器
headers
key value
Content-Type text/html
Content-Length 42123
Server BirdServer
... ...
*/
Set<Map.Entry<String,String>> entrySet = headers.entrySet();
for(Map.Entry<String,String> e : entrySet){
String name = e.getKey();
String value = e.getValue();
println(name+": "+value);
}
//单独发送回车+换行,表示响应头发送完毕
println("");
}
//发送响应正文
private void sendContent() throws IOException {
FileInputStream fis = new FileInputStream(contentFile);
OutputStream out = socket.getOutputStream();
byte[] buf = new byte[1024*10];//10kb
int d;//记录每次实际读取的数据量
while( (d = fis.read(buf)) !=-1){
out.write(buf,0,d);
}
}
/**
* V7:将ClientHandler中发送响应的工作全部移动到这里,println方法也是。
* 向客户端发送一行字符串
* @param line
*/
private void println(String line) throws IOException {
OutputStream out = socket.getOutputStream();
byte[] data = line.getBytes(StandardCharsets.ISO_8859_1);
out.write(data);
out.write(13);//发送回车符
out.write(10);//发送换行符
}
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
public String getStatusReason() {
return statusReason;
}
public void setStatusReason(String statusReason) {
this.statusReason = statusReason;
}
public File getContentFile() {
return contentFile;
}
/**
* 设置响应正文对应的实体文件,该方法中会自动根据该文件添加对应的两个响应头:
* Content-Type和Content-Length
* @param contentFile
*/
public void setContentFile(File contentFile) {
this.contentFile = contentFile;
addHeader("Content-Type",mftm.getContentType(contentFile));
addHeader("Content-Length",contentFile.length()+"");
}
/**
* 添加一个响应头
* @param name
* @param value
*/
public void addHeader(String name,String value){
headers.put(name,value);
}
}
通过V12和V13两个版本完成用户注册业务
用户注册业务的大致流程:
- 用户访问注册页面,并在页面上输入注册信息后点击注册按钮
- 数据提交发到服务端,服务端解析页面提交上来的数据
- 根据解析出来的数据进行响应的注册处理
- 给用户回复一个注册处理结果的页面(注册成功或失败)
这里涉及的知识点:
页面表单GET请求提交两个问题需要在V12和V13中解决
- 表单提交后
HttpServletRequest
是如何解析的,这个在V12中处理DispatcherServlet
是如何区分请求业务还是其他静态资源(页面,图片等),并且是如何在处理业务时调用的Controller的对应方法(V13中解决)本版本完成表单的提交以及请求的解析工作。这个工作是通用操作,无论将来处理何种业务,解析表单数据的方式都是相同的。
实现:
重构HttpServletRequest的解析工作,添加对表单数据的解析。
- 定义三个新的属性:
String requestURI
,String queryString
,Map parameters
分别保存抽象路径中的请求部分,参数部分和每一组参数- 定义
parseUri
方法,进一步解析抽象路径中包含的参数内容- 在解析请求的方法中解析出三部分后调用
parseUri
进一步解析uri
package com.birdboot.core;
import com.birdboot.http.EmptyRequestException;
import com.birdboot.http.HttpServletRequest;
import com.birdboot.http.HttpServletResponse;
import java.io.*;
import java.net.Socket;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* 该线程任务负责与指定的客户端进行HTTP交互
* HTTP协议要求浏览器与服务端采取"一问一答"的模式。对此,这里的处理流程分为三步:
* 1:解析请求
* 2:处理请求
* 3:发送响应
*/
public class ClientHandler implements Runnable {
private Socket socket;
public ClientHandler(Socket socket) {
this.socket = socket;
}
public void run() {
try {
//1 解析请求
HttpServletRequest request = new HttpServletRequest(socket);
HttpServletResponse response = new HttpServletResponse(socket);
//2 处理请求
//V8改造:将处理请求的操作移动到DispatcherServlet的service方法中并调用
DispatcherServlet servlet = new DispatcherServlet();
servlet.service(request,response);
//3 发送响应
response.response();
} catch (IOException e) {
e.printStackTrace();
} catch (EmptyRequestException e) {
} finally {
//HTTP协议要求浏览器与服务端交互完毕后要断开连接
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.birdboot.http;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
/**
* V4:新增内容
* 请求对象
* 该类的每一个实例用于表示浏览器发送过来的一个HTTP请求
* HTTP协议要求请求的格式由三部分构成:请求行,消息头,消息正文
*/
public class HttpServletRequest {
private Socket socket;
//请求行相关信息
private String method;//请求方式
private String uri;//抽象路径
private String protocol;//协议版本
/*
V12新增内容
例如:
uri--->/regUser?username=fancq&password=123456&nickname=chuanqi&age=22
新添加的三个属性最终应当保存的信息如下
requestURI:/regUser
queryString:username=fancq&password=123456&nickname=chuanqi&age=22
parameters:
key value
username fancq
password 123456
nickname chuanqi
age 22
*/
private String requestURI;//保存uri中"?"左侧的请求路径部分
private String queryString;//保存uri中"?"右侧的参数部分
private Map<String,String> parameters = new HashMap<>();//保存每一组参数
//消息头相关信息 key:消息头名字 value:消息头对应的值
private Map<String,String> headers = new HashMap<>();
public HttpServletRequest(Socket socket) throws IOException, EmptyRequestException {
this.socket = socket;
//1.1解析请求行
parseRequestLine();
//1.2解析消息头
parseHeaders();
//1.3解析消息正文
parseContent();
}
//解析请求行
private void parseRequestLine() throws IOException, EmptyRequestException {
String line = readLine();
if(line.isEmpty()){//如果请求行是个空字符串,则说明本次为空请求
throw new EmptyRequestException();
}
System.out.println("请求行:"+line);
//将请求行按照空格("\s"在正则表达式中表示一个空白字符,包含空格)拆分为三部分
String[] data = line.split("\\s");
method = data[0];
uri = data[1];
protocol = data[2];
parseURI();//进一步解析uri
System.out.println("method:"+method);
System.out.println("uri:"+uri);
System.out.println("protocol:"+protocol);
}
//进一步解析uri
private void parseURI(){
/*
uri有两种情况:
1:不含有参数的
例如: /index.html
直接将uri的值赋值给requestURI即可.
2:含有参数的
例如:/regUser?username=fancq&password=123456&nickname=chuanqi&age=22
将uri中"?"左侧的请求部分赋值给requestURI
将uri中"?"右侧的参数部分赋值给queryString
将参数部分首先按照"&"拆分出每一组参数,再将每一组参数按照"="拆分为参数名与参数值
并将参数名作为key,参数值作为value存入到parameters中。
requestURI:/regUser
queryString:username=fancq&password=123456&nickname=chuanqi&age=22
parameters:
key value
username fancq
password 123456
nickname chuanqi
age 22
如果表单某个输入框没有输入信息,那么存入parameters时对应的值应当保存为空字符串
当页面form表单中指定了action="/regUser",但是该表单中所有输入框均没有指定名字时:
/regUser?
*/
/*
/index.html data:[/index.html]
/regUser?xxxxx data:[/regUser, xxxxx]
/regUser? data:[/regUser]
*/
String[] data = uri.split("\\?");
requestURI = data[0];
if(data.length>1){
queryString = data[1];
/*
进一步拆分参数
先拆分出每一组参数,将queryString按照"&"进行拆分
username=fancq&password=123456&nickname=chuanqi&age=22
*/
//paraArr:[username=fancq, password=123456, nickname=chuanqi, age=22]
String[] paraArr = queryString.split("&");
/*
再遍历paraArr进一步拆分每一组参数的参数名和参数值
每一组参数按照"="进行拆分
*/
//para:username=fancq
for(String para : paraArr){
/*
username=fancq 用户在浏览器该输入框输入信息
username= 用户在浏览器该输入框没有输入信息
*/
String[] arr = para.split("=",2);
parameters.put(arr[0],arr[1]);
}
}
System.out.println("requestURI:"+requestURI);
System.out.println("queryString:"+queryString);
System.out.println("parameters:"+parameters);
}
//解析消息头
private void parseHeaders() throws IOException {
while(true) {
String line = readLine();
if(line.isEmpty()){//如果读取到了空行
break;
}
System.out.println("消息头:" + line);
String[] data = line.split(":\\s");
headers.put(data[0],data[1]);
}
System.out.println("headers:"+headers);
}
//解析消息正文
private void parseContent(){}
/**
* 通过socket获取的输入流读取客户端发送过来的一行字符串
* @return
*/
private String readLine() throws IOException {//通常被重用的代码不自己处理异常
//对一个socket实例调用多次getInputStream()返回的始终是同一条输入流。而输出流也是如此
InputStream in = socket.getInputStream();
int d;
char pre='a',cur='a';//pre表示上次读取的字符,cur表示本次读取的字符
StringBuilder builder = new StringBuilder();//保存读取后的所有字符
while((d = in.read())!=-1){
cur = (char)d;//本次读取的字符
if(pre==13 && cur==10){//是否连续读取到了回车+换行
break;
}
builder.append(cur);//将本次读取的字符拼接
pre=cur;//在进行下次读取前,将本次读取的字符保存到"上次读取的字符"中
}
return builder.toString().trim();
}
public String getMethod() {
return method;
}
public String getUri() {
return uri;
}
public String getProtocol() {
return protocol;
}
/**
* 根据给定的消息头的名字获取对应消息头的值
* @param name
* @return
*/
public String getHeader(String name) {
return headers.get(name);
}
public String getRequestURI() {
return requestURI;
}
public String getQueryString() {
return queryString;
}
public String getParameter(String name) {
return parameters.get(name);
}
}
package com.birdboot.http;
import javax.activation.MimetypesFileTypeMap;
import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* V7新增内容:
* 响应对象
* 该类的每一个实例用于表示服务端给客户端发送的一个HTTP的响应
* HTTP协议要求一个响应由三部分构成:状态行,响应头,响应正文
*/
public class HttpServletResponse {
private static MimetypesFileTypeMap mftm = new MimetypesFileTypeMap();
private Socket socket;
//状态行相关信息
private int statusCode = 200;//状态代码
private String statusReason = "OK";//状态描述
//响应头相关信息 key:响应头的名字 value:响应头的值
private Map<String,String> headers = new HashMap<>();
//响应正文相关信息
private File contentFile;//响应正文对应的实体文件
public HttpServletResponse(Socket socket){
this.socket = socket;
}
/**
* 该方法用于将当前响应对象内容以标准的HTTP响应格式发送给客户端
*/
public void response() throws IOException {
//3.1发送状态行
sendStatusLine();
//3.2发送响应头
sendHeaders();
//3.3发送响应正文
sendContent();
}
//发送状态行
private void sendStatusLine() throws IOException {
println("HTTP/1.1"+" "+statusCode+" "+statusReason);
}
//发送响应头
private void sendHeaders() throws IOException {
/*
遍历headers将所有待发送的响应头发送给浏览器
headers
key value
Content-Type text/html
Content-Length 42123
Server BirdServer
... ...
*/
Set<Map.Entry<String,String>> entrySet = headers.entrySet();
for(Map.Entry<String,String> e : entrySet){
String name = e.getKey();
String value = e.getValue();
println(name+": "+value);
}
//单独发送回车+换行,表示响应头发送完毕
println("");
}
//发送响应正文
private void sendContent() throws IOException {
FileInputStream fis = new FileInputStream(contentFile);
OutputStream out = socket.getOutputStream();
byte[] buf = new byte[1024*10];//10kb
int d;//记录每次实际读取的数据量
while( (d = fis.read(buf)) !=-1){
out.write(buf,0,d);
}
}
/**
* V7:将ClientHandler中发送响应的工作全部移动到这里,println方法也是。
* 向客户端发送一行字符串
* @param line
*/
private void println(String line) throws IOException {
OutputStream out = socket.getOutputStream();
byte[] data = line.getBytes(StandardCharsets.ISO_8859_1);
out.write(data);
out.write(13);//发送回车符
out.write(10);//发送换行符
}
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
public String getStatusReason() {
return statusReason;
}
public void setStatusReason(String statusReason) {
this.statusReason = statusReason;
}
public File getContentFile() {
return contentFile;
}
/**
* 设置响应正文对应的实体文件,该方法中会自动根据该文件添加对应的两个响应头:
* Content-Type和Content-Length
* @param contentFile
*/
public void setContentFile(File contentFile) {
this.contentFile = contentFile;
addHeader("Content-Type",mftm.getContentType(contentFile));
addHeader("Content-Length",contentFile.length()+"");
}
/**
* 添加一个响应头
* @param name
* @param value
*/
public void addHeader(String name,String value){
headers.put(name,value);
}
}
package com.birdboot.core;
import com.birdboot.http.HttpServletRequest;
import com.birdboot.http.HttpServletResponse;
import java.io.File;
import java.net.URISyntaxException;
/**
* V8新增内容:
* 该类是SpringMVC框架与Tomcat整合时的一个关键类
* Tomcat处理业务原生的都是调用继承了HttpServlet的类来完成,此时需要进行很多配置
* 以及使用时要作很多重复性劳动。
* SpringMVC框架提供的该类也是继承了HttpServlet的,使用它来接收处理请求的工作。
*/
public class DispatcherServlet {
private static File baseDir;//类加载路径
private static File staticDir;//类加载路径下的static目录
static{
try {
//定位当前项目的类加载路径
baseDir = new File(
DispatcherServlet.class.getClassLoader().getResource(".").toURI()
);
//定位类加载路径下的static目录
staticDir = new File(baseDir, "static");
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
/**
* service方法实际上是当我们继承了HttpServlet后必须重写的方法
* 该方法要求接收两个参数:请求对象与响应对象。
* Tomcat在处理请求时就是调用某个Servlet的service方法并将请求与响应对象传入
* 来让其完成处理工作的。
*/
public void service(HttpServletRequest request, HttpServletResponse response){
//获取请求的抽象路径
String path = request.getUri();
System.out.println(path);
File file = new File(staticDir, path);
if(file.isFile()){
//由于响应对象中状态代码和描述默认值为200,OK因此正确情况下不用再设置
response.setContentFile(file);
//设置响应头
response.addHeader("Server","BirdServer");
}else{
response.setStatusCode(404);
response.setStatusReason("NotFound");
file = new File(staticDir,"404.html");
response.setContentFile(file);
response.addHeader("Server","BirdServer");
}
}
}
package com.birdboot.controller;
import com.birdboot.http.HttpServletRequest;
import com.birdboot.http.HttpServletResponse;
/**
* 处理与用户相关的业务
*/
public class UserController {
//处理"/regUser"这个请求
public void reg(HttpServletRequest request, HttpServletResponse response){
System.out.println("开始处理用户注册");
}
}
package com.birdboot.core;
import com.birdboot.http.EmptyRequestException;
import com.birdboot.http.HttpServletRequest;
import com.birdboot.http.HttpServletResponse;
import java.io.*;
import java.net.Socket;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* 该线程任务负责与指定的客户端进行HTTP交互
* HTTP协议要求浏览器与服务端采取"一问一答"的模式。对此,这里的处理流程分为三步:
* 1:解析请求
* 2:处理请求
* 3:发送响应
*/
public class ClientHandler implements Runnable {
private Socket socket;
public ClientHandler(Socket socket) {
this.socket = socket;
}
public void run() {
try {
//1 解析请求
HttpServletRequest request = new HttpServletRequest(socket);
HttpServletResponse response = new HttpServletResponse(socket);
//2 处理请求
//V8改造:将处理请求的操作移动到DispatcherServlet的service方法中并调用
DispatcherServlet servlet = new DispatcherServlet();
servlet.service(request,response);
//3 发送响应
response.response();
} catch (IOException e) {
e.printStackTrace();
} catch (EmptyRequestException e) {
} finally {
//HTTP协议要求浏览器与服务端交互完毕后要断开连接
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.birdboot.http;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
/**
* V4:新增内容
* 请求对象
* 该类的每一个实例用于表示浏览器发送过来的一个HTTP请求
* HTTP协议要求请求的格式由三部分构成:请求行,消息头,消息正文
*/
public class HttpServletRequest {
private Socket socket;
//请求行相关信息
private String method;//请求方式
private String uri;//抽象路径
private String protocol;//协议版本
/*
V12新增内容
例如:
uri--->/regUser?username=fancq&password=123456&nickname=chuanqi&age=22
新添加的三个属性最终应当保存的信息如下
requestURI:/regUser
queryString:username=fancq&password=123456&nickname=chuanqi&age=22
parameters:
key value
username fancq
password 123456
nickname chuanqi
age 22
*/
private String requestURI;//保存uri中"?"左侧的请求路径部分
private String queryString;//保存uri中"?"右侧的参数部分
private Map<String,String> parameters = new HashMap<>();//保存每一组参数
//消息头相关信息 key:消息头名字 value:消息头对应的值
private Map<String,String> headers = new HashMap<>();
public HttpServletRequest(Socket socket) throws IOException, EmptyRequestException {
this.socket = socket;
//1.1解析请求行
parseRequestLine();
//1.2解析消息头
parseHeaders();
//1.3解析消息正文
parseContent();
}
//解析请求行
private void parseRequestLine() throws IOException, EmptyRequestException {
String line = readLine();
if(line.isEmpty()){//如果请求行是个空字符串,则说明本次为空请求
throw new EmptyRequestException();
}
System.out.println("请求行:"+line);
//将请求行按照空格("\s"在正则表达式中表示一个空白字符,包含空格)拆分为三部分
String[] data = line.split("\\s");
method = data[0];
uri = data[1];
protocol = data[2];
parseURI();//进一步解析uri
System.out.println("method:"+method);
System.out.println("uri:"+uri);
System.out.println("protocol:"+protocol);
}
//进一步解析uri
private void parseURI(){
/*
uri有两种情况:
1:不含有参数的
例如: /index.html
直接将uri的值赋值给requestURI即可.
2:含有参数的
例如:/regUser?username=fancq&password=123456&nickname=chuanqi&age=22
将uri中"?"左侧的请求部分赋值给requestURI
将uri中"?"右侧的参数部分赋值给queryString
将参数部分首先按照"&"拆分出每一组参数,再将每一组参数按照"="拆分为参数名与参数值
并将参数名作为key,参数值作为value存入到parameters中。
requestURI:/regUser
queryString:username=fancq&password=123456&nickname=chuanqi&age=22
parameters:
key value
username fancq
password 123456
nickname chuanqi
age 22
如果表单某个输入框没有输入信息,那么存入parameters时对应的值应当保存为空字符串
当页面form表单中指定了action="/regUser",但是该表单中所有输入框均没有指定名字时:
/regUser?
*/
/*
/index.html data:[/index.html]
/regUser?xxxxx data:[/regUser, xxxxx]
/regUser? data:[/regUser]
*/
String[] data = uri.split("\\?");
requestURI = data[0];
if(data.length>1){
queryString = data[1];
/*
进一步拆分参数
先拆分出每一组参数,将queryString按照"&"进行拆分
username=fancq&password=123456&nickname=chuanqi&age=22
*/
//paraArr:[username=fancq, password=123456, nickname=chuanqi, age=22]
String[] paraArr = queryString.split("&");
/*
再遍历paraArr进一步拆分每一组参数的参数名和参数值
每一组参数按照"="进行拆分
*/
//para:username=fancq
for(String para : paraArr){
/*
username=fancq 用户在浏览器该输入框输入信息
username= 用户在浏览器该输入框没有输入信息
*/
String[] arr = para.split("=",2);
parameters.put(arr[0],arr[1]);
}
}
System.out.println("requestURI:"+requestURI);
System.out.println("queryString:"+queryString);
System.out.println("parameters:"+parameters);
}
//解析消息头
private void parseHeaders() throws IOException {
while(true) {
String line = readLine();
if(line.isEmpty()){//如果读取到了空行
break;
}
System.out.println("消息头:" + line);
String[] data = line.split(":\\s");
headers.put(data[0],data[1]);
}
System.out.println("headers:"+headers);
}
//解析消息正文
private void parseContent(){}
/**
* 通过socket获取的输入流读取客户端发送过来的一行字符串
* @return
*/
private String readLine() throws IOException {//通常被重用的代码不自己处理异常
//对一个socket实例调用多次getInputStream()返回的始终是同一条输入流。而输出流也是如此
InputStream in = socket.getInputStream();
int d;
char pre='a',cur='a';//pre表示上次读取的字符,cur表示本次读取的字符
StringBuilder builder = new StringBuilder();//保存读取后的所有字符
while((d = in.read())!=-1){
cur = (char)d;//本次读取的字符
if(pre==13 && cur==10){//是否连续读取到了回车+换行
break;
}
builder.append(cur);//将本次读取的字符拼接
pre=cur;//在进行下次读取前,将本次读取的字符保存到"上次读取的字符"中
}
return builder.toString().trim();
}
public String getMethod() {
return method;
}
public String getUri() {
return uri;
}
public String getProtocol() {
return protocol;
}
/**
* 根据给定的消息头的名字获取对应消息头的值
* @param name
* @return
*/
public String getHeader(String name) {
return headers.get(name);
}
public String getRequestURI() {
return requestURI;
}
public String getQueryString() {
return queryString;
}
public String getParameter(String name) {
return parameters.get(name);
}
}
package com.birdboot.http;
import javax.activation.MimetypesFileTypeMap;
import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* V7新增内容:
* 响应对象
* 该类的每一个实例用于表示服务端给客户端发送的一个HTTP的响应
* HTTP协议要求一个响应由三部分构成:状态行,响应头,响应正文
*/
public class HttpServletResponse {
private static MimetypesFileTypeMap mftm = new MimetypesFileTypeMap();
private Socket socket;
//状态行相关信息
private int statusCode = 200;//状态代码
private String statusReason = "OK";//状态描述
//响应头相关信息 key:响应头的名字 value:响应头的值
private Map<String,String> headers = new HashMap<>();
//响应正文相关信息
private File contentFile;//响应正文对应的实体文件
public HttpServletResponse(Socket socket){
this.socket = socket;
}
/**
* 该方法用于将当前响应对象内容以标准的HTTP响应格式发送给客户端
*/
public void response() throws IOException {
//3.1发送状态行
sendStatusLine();
//3.2发送响应头
sendHeaders();
//3.3发送响应正文
sendContent();
}
//发送状态行
private void sendStatusLine() throws IOException {
println("HTTP/1.1"+" "+statusCode+" "+statusReason);
}
//发送响应头
private void sendHeaders() throws IOException {
/*
遍历headers将所有待发送的响应头发送给浏览器
headers
key value
Content-Type text/html
Content-Length 42123
Server BirdServer
... ...
*/
Set<Map.Entry<String,String>> entrySet = headers.entrySet();
for(Map.Entry<String,String> e : entrySet){
String name = e.getKey();
String value = e.getValue();
println(name+": "+value);
}
//单独发送回车+换行,表示响应头发送完毕
println("");
}
//发送响应正文
private void sendContent() throws IOException {
if(contentFile!=null) {
FileInputStream fis = new FileInputStream(contentFile);
OutputStream out = socket.getOutputStream();
byte[] buf = new byte[1024 * 10];//10kb
int d;//记录每次实际读取的数据量
while ((d = fis.read(buf)) != -1) {
out.write(buf, 0, d);
}
}
}
/**
* V7:将ClientHandler中发送响应的工作全部移动到这里,println方法也是。
* 向客户端发送一行字符串
* @param line
*/
private void println(String line) throws IOException {
OutputStream out = socket.getOutputStream();
byte[] data = line.getBytes(StandardCharsets.ISO_8859_1);
out.write(data);
out.write(13);//发送回车符
out.write(10);//发送换行符
}
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
public String getStatusReason() {
return statusReason;
}
public void setStatusReason(String statusReason) {
this.statusReason = statusReason;
}
public File getContentFile() {
return contentFile;
}
/**
* 设置响应正文对应的实体文件,该方法中会自动根据该文件添加对应的两个响应头:
* Content-Type和Content-Length
* @param contentFile
*/
public void setContentFile(File contentFile) {
this.contentFile = contentFile;
addHeader("Content-Type",mftm.getContentType(contentFile));
addHeader("Content-Length",contentFile.length()+"");
}
/**
* 添加一个响应头
* @param name
* @param value
*/
public void addHeader(String name,String value){
headers.put(name,value);
}
}
package com.birdboot.core;
import com.birdboot.controller.UserController;
import com.birdboot.http.HttpServletRequest;
import com.birdboot.http.HttpServletResponse;
import java.io.File;
import java.net.URISyntaxException;
/**
* V8新增内容:
* 该类是SpringMVC框架与Tomcat整合时的一个关键类
* Tomcat处理业务原生的都是调用继承了HttpServlet的类来完成,此时需要进行很多配置
* 以及使用时要作很多重复性劳动。
* SpringMVC框架提供的该类也是继承了HttpServlet的,使用它来接收处理请求的工作。
*/
public class DispatcherServlet {
private static File baseDir;//类加载路径
private static File staticDir;//类加载路径下的static目录
static{
try {
//定位当前项目的类加载路径
baseDir = new File(
DispatcherServlet.class.getClassLoader().getResource(".").toURI()
);
//定位类加载路径下的static目录
staticDir = new File(baseDir, "static");
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
/**
* service方法实际上是当我们继承了HttpServlet后必须重写的方法
* 该方法要求接收两个参数:请求对象与响应对象。
* Tomcat在处理请求时就是调用某个Servlet的service方法并将请求与响应对象传入
* 来让其完成处理工作的。
*/
public void service(HttpServletRequest request, HttpServletResponse response){
//获取请求的抽象路径
//不能在使用uri判断请求了,因为uri可能含参数,内容不固定。
String path = request.getRequestURI();
System.out.println(path);
//判断该请求是否为请求一个业务
if("/regUser".equals(path)){
UserController controller = new UserController();
controller.reg(request, response);
}else {
File file = new File(staticDir, path);
if (file.isFile()) {
//由于响应对象中状态代码和描述默认值为200,OK因此正确情况下不用再设置
response.setContentFile(file);
//设置响应头
response.addHeader("Server", "BirdServer");
} else {
response.setStatusCode(404);
response.setStatusReason("NotFound");
file = new File(staticDir, "404.html");
response.setContentFile(file);
response.addHeader("Server", "BirdServer");
}
}
}
}