第一章 HTTP消息机制和经典的同步I/O流

1.1 HTTP消息机制
1.1.1 结构
一个HTTP消息内容一般由头(header)部分和可选择的请求体(body)部分组成。请求消息由请求行和请求头的属性组成。
 响应消息由状态行和响应头的属性组成。所有的HTTP消息必须包含协议版本号。一些消息可以选择性的包含请求/响应
 体。HttpCore定义了HTTP消息模型来处理HTTP消息,并且提供了大量的序列化/反序列号HTTP消息元素的扩展功能。
1.1.2. 基础操作
1.1.2.1. HTTP请求消息机制

HTTP请求是一个从客户端发送消息到服务端的过程。

下面这个例子第一行的方法是一个包含请求方式(GET/POST...)和请求资源路径(uri)和使用HTTP协议的版本信息。

HttpRequest request = new BasicHttpRequest("GET", "/",HttpVersion.HTTP_1_1);
System.out.println(request.getRequestLine().getMethod());
System.out.println(request.getRequestLine().getUri());
System.out.println(request.getProtocolVersion());
System.out.println(request.getRequestLine().toString());

输出
GET
/
HTTP/1.1
GET / HTTP/1.1
1.1.2.2. HTTP 响应消息机制
HTTP响应当服务端接受客户端的请求并且处理完之后返回给客户端的消息.下面的例子中的第一行包含了协议版本信息,响应状态码,响应状态的解释.
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_OK, "OK");
System.out.println(response.getProtocolVersion());
System.out.println(response.getStatusLine().getStatusCode());
System.out.println(response.getStatusLine().getReasonPhrase());
System.out.println(response.getStatusLine().toString());

输出
HTTP/1.1
200
OK
HTTP/1.1 200 OK
 
1.1.2.3. HTTP消息常用的属性和方法
一个HTTP消息包含很多描述消息的属性,例如:内容长度,内容类型等等。HTTP消息提供了检索、添加、删除和枚举等方法。
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie","c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie","c2=b; path=\"/\", c3=c; domain=\"localhost\"");
Header h1 = response.getFirstHeader("Set-Cookie");
System.out.println(h1);
Header h2 = response.getLastHeader("Set-Cookie");
System.out.println(h2);
Header[] hs = response.getHeaders("Set-Cookie");
System.out.println(hs.length);

输出
Set-Cookie: c1=a; path=/; domain=localhost
Set-Cookie: c2=b; path="/", c3=c; domain="localhost"
2
可以使用HeaderIterator迭代出所有指定类型的消息头
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie","c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie","c2=b; path=\"/\", c3=c; domain=\"localhost\"");

HeaderIterator it = response.headerIterator("Set-Cookie");

while (it.hasNext()) {
   System.out.println(it.next());
}
输出
Set-Cookie: c1=a; path=/; domain=localhost
Set-Cookie: c2=b; path="/", c3=c; domain="localhost"
它也提供了很多种方便API来解析HTTP消息。
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie","c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie","c2=b; path=\"/\", c3=c; domain=\"localhost\"");

HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator("Set-Cookie"));

while (it.hasNext()) {
   HeaderElement elem = it.nextElement();
   System.out.println(elem.getName() + " = " + elem.getValue());
   NameValuePair[] params = elem.getParameters();
   for (int i = 0; i < params.length; i++) {
       System.out.println(" " + params[i]);
   }
}
输出
c1 = a
path=/
domain=localhost
c2 = b
path=/
c3 = c
domain=localhost
 
HTTP消息头被以数组的方式缓存在消息头元素里面,只有当用户使用的时候才会延迟加载出来。
1.1.3. HTTP 实体
HTTP消息会装载请求/响应体的内容实体。实体是可选的,封装在请求/响应里面。请求会被封装成请求实体。HTTP协议规范
规定了两个实体封装方法:POST和PUT。请求通常被封装成内容实体。当然也有代表异常规则的,比如响应的头方法和204 没有内容
304没有修饰 205重置响应内容。

HttpCore定义了三种实体,区别是他们的内容来源:
流(streamed):这种内容是从一个流或者动态请求来获得的。一般,这种方式的获取是从一个连接来的。流实体一般不能重复使用。
 自包含(self-contained):这种一般是在内存中或者从一个连接或其他实体获得的。自包含的实体一般是可以重复使用的。
包装(wrapping):这种内容是从其他实体来的。

这种带有实体的对于连接管理是很重要的。对于一个使用HttpCore框架来创建和发送实体的,流和自包含差别不是很大,也不是很重要。
这种情况下我们建议你使用不可重复的实体作为流,可重复的作为自包含的。

1.1.3.1. 可重复的实体
一个实体可重复意味着你可以重复读取他的内容。仅仅自包含的实体可以重复的(例如 ByteArrayEntity 或 StringEntity)。
1.1.3.2. 使用HTTP实体
一个实体可以用于二进制和字符内容,它支持字符编码(支持后者,比如字符内容)。当执行一个请求或者当请求成功并且服
务器响应成功到客户端的时候实体被创建并且包含被封装的内容。

有两种方式可以从实体获得消息内容,一种是通过输入流使用HttpEntity.getContent()方法,得到一个输入流java.io.InputStream。
另一种是通过提供一个输出流个体HttpEntity.writeTo(OutputStream)方法,它会自动的将输入流返回给提供的输出流。

EntityUtils工具类提供了几个简便的方法可以从实体获得消息内容和信息。这样就不必直接操作输入流java.io.InputStream了,
可以直接通过此方法获得完整的消息体的字符串或者直接数组。

当传入的消息实体被接受否,可以通过HttpEntity.getContentType()和HttpEntity.getContentLength()方法来读取一些基本
消息头信息例如:Content-Type(内容类型)、Content-Length(内容长度)(如果他们是有效的)。因为Content-Type头可能包含
文本类型像:text/plain、text/html的字符编码,可以使用HttpEntity.getContentEncoding()方法读取。如果消息头是不可见的
会返回-1,并且内容类型返回的是null。如果Content-Type头是可见的,一个消息头对象会被返回。

当创建输出消息时候,需要指定消息内容和编码
StringEntity myEntity = new StringEntity("important message",Consts.UTF_8);

System.out.println(myEntity.getContentType());
System.out.println(myEntity.getContentLength());
System.out.println(EntityUtils.toString(myEntity));
System.out.println(EntityUtils.toByteArray(myEntity).length);

输出
Content-Type: text/plain; charset=UTF-8
17
important message
17


1.1.3.3. 确保释放系统资源
为了确保释放系统资源,必须关闭和实体关联的流
HttpResponse response;
HttpEntity entity = response.getEntity();
if (entity != null) {
   InputStream instream = entity.getContent();
   try {
       // do something useful
   } finally {
       instream.close();
   }
}
请注意HttpEntity.writeTo(OutputStream)方法在使用完输出之后也需要正确的释放资源。如果这个方法使用HttpEntity.getContent()
获得一个输入流java.io.InputStream实例也需要在finally语句块里面关闭。
当使用流实体的时候,可以使用EntityUtils.consume(HttpEntity)这个方法来确保流被完全的并且底层也被关闭。
EntityUtils.consume(entity);//这样交给框架来关闭,不用再手动关闭了。
1.1.4. 创建实体
有几个方式可以创建实体。HttpCore提供以下的实现:
BasicHttpEntity
ByteArrayEntity
StringEntity
InputStreamEntity
FileEntity
EntityTemplate
HttpEntityWrapper
BufferedHttpEntity
1.1.4.1. BasicHttpEntity
正如名字那样的理解,这个基础的实体代表底层流。一般情况下这个类用于接收HTTP消息。这个实体类含有一个空的构造器。在构造玩之后,
这个实体没有内容,并且有一个负数代表的长度。需要设置内容流,和可选的长度。可以使用BasicHttpEntity.setContent(InputStream)和
BasicHttpEntity.setContent(long)方法分别的设置。
BasicHttpEntity myEntity = new BasicHttpEntity();
myEntity.setContent(someInputStream);
myEntity.setContentLength(340); // sets the length to 340
1.1.4.2. ByteArrayEntity
ByteArrayEntity是一个自包含,代表它的实体内容是从一个字节数组获得的。提供字节数组构造函数。
ByteArrayEntity myEntity = new ByteArrayEntity(new byte[] {1,2,3},ContentType.APPLICATION_OCTET_STREAM);
1.1.4.3. StringEntity
StringEntity是一个自包含,可重复的实体包含java.lang.String对象。他有三个构造函数,一个带有java.lang.String简单的
构造函数,第二个包含一个字符编码,第三个是指定的内容。
StringBuilder sb = new StringBuilder();
Map<String, String> env = System.getenv();
for (Map.Entry<String, String> envEntry : env.entrySet()) {
   sb.append(envEntry.getKey())
           .append(": ").append(envEntry.getValue())
           .append("\r\n");
}

// 不包含编码的构造函数 (默认编码是 ISO-8859-1)
HttpEntity myEntity1 = new StringEntity(sb.toString());

// 第二种包含编码 (文本类型: "text/plain")
HttpEntity myEntity2 = new StringEntity(sb.toString(), Consts.UTF_8);

// 第三钟包含文本类型、编码
HttpEntity myEntity3 = new StringEntity(sb.toString(),ContentType.create("text/plain", Consts.UTF_8));
1.1.4.4. InputStreamEntity
InputStreamEntity是一个从输入流获得不可重复的流实体。构造方法提供输入流和内容长度。用长度来限制从输入流获取的内容长度。如果长度匹配
内容符合有效的,那么所有的数据会被接受到流实体里面。
InputStream instream = getSomeInputStream();
InputStreamEntity myEntity = new InputStreamEntity(instream, 16);
1.1.4.5. FileEntity
FileEntity是一个自包含可重复的从一个文件获得内容的实体。使用这个主要是用来传输一些大的文件类型的,比如压缩文件。需要设置文件类型application/zip,
XML applicatoin/xml.
HttpEntity entity = new FileEntity(staticFile,ContentType.create("application/java-archive"));
1.1.4.6. HttpEntityWrapper
这是一个可以创建包装实体的基础类。这个封装实体包含一个封装实体(HttpEntity)的引用和所有调用该实体(HttpEntity)方法。使用的时候,应该重写一些该实体封装类的
方法,需要自己来实现。

1.1.4.7. BufferedHttpEntity(*****)
BufferedHttpEntity是HttpEntityWrapper的派生类。构造它需要提供另一个实体。它从给定的实体读取内容,并且缓存在内容中。这意味着它可以从一个不可重复
读的实体创建出可以重复读取内容的实体。
myNonRepeatableEntity.setContent(someInputStream);
BufferedHttpEntity myBufferedEntity = new BufferedHttpEntity(myNonRepeatableEntity);
1.2 HTTP连接
HTTP连接主要是序列化和反序列化。一般很少直接使用HTTP连接。有高级别的协议组件用于执行和处理HTTP请求。但是在有些情况下需要直接使用HTTP连接
比如:获得连接属性的连接状态、socket超时、本地和远程地址。
需要记住的是HTTP连接不是线程安全的。我们强烈建议针对一个线程的HTTP连接对象应该被限制。唯一的HttpConnection接口和他的子接口是安全的,可以
从另一个线程中调用HttpConnection.shutdown();就是使用HttpConnection的子类是线程安全的。
1.2.1. 使用HTTP连接

你可能感兴趣的:(httpclient,http协议,中文版,教程)