简介
jodd-http是一个微型简约的http client,然而简单而且方便,使用它可以轻松的实现发送请求和读取响应,由于个人工作需要,现在对其进行部分源码分析,以6.3.0版本为例
通过maven引入
<dependency>
<groupId>org.jodd</groupId>
<artifactId>jodd-http</artifactId>
<version>6.3.0</version>
</dependency>
例子1
写一个简单的get请求
HttpRequest httpRequest = HttpRequest.get("http://baidu.com");
HttpResponse httpResponse = httpRequest.send();
System.out.println(httpResponse.bodyText());
源码分析1
创建了一个httpRequest对象
public static HttpRequest get(final String destination) {
return new HttpRequest()
.method(HttpMethod.GET)
.set(destination);
}
initRequest
初始化,默认为非长连接,即将headers中的Connection设置为Close,同时将Defaults.headers中的内容复制到this.headers中,即将设置User-Agent为Jodd HTTP
public HttpRequest() {
initRequest();
}
protected void initRequest() {
connectionKeepAlive(false);
if (Defaults.headers.size() > 0) {
for (Map.Entry<String, String> entry : Defaults.headers) {
this.headers.add(entry.getKey(), entry.getValue());
}
}
}
method
设置http的请求方法
public HttpRequest method(final HttpMethod httpMethod) {
this.method = httpMethod.name();
return this;
}
set
只保留核心流程,根据传入参数设置httpRequest的方法,协议,端口,主机,路径,参数
public HttpRequest set(String destination) {
// 根据传入参数重新设置请求方法
try {
final HttpMethod httpMethod = HttpMethod.valueOf(method);
this.method = httpMethod.name();
destination = destination.substring(ndx + 1);
}
catch (final IllegalArgumentException ignore) {
// unknown http method
}
}
// 设置请求协议
if (ndx != -1) {
protocol = destination.substring(0, ndx);
destination = destination.substring(ndx + 3);
}
String hostToSet = destination.substring(0, ndx);
destination = destination.substring(ndx);
// 设置请求的端口
if (ndx == -1) {
port = Defaults.DEFAULT_PORT;
} else {
port = Integer.parseInt(hostToSet.substring(ndx + 1));
hostToSet = hostToSet.substring(0, ndx);
}
// 设置请求的主机
host(hostToSet);
}
// 设置请求的路径和参数
path(destination);
return this;
}
send
// 默认情况下不需要重定向
public HttpResponse send() {
if (!followRedirects) {
return _send();
}
}
// 一开始没有httpConnection
private HttpResponse _send() {
if (httpConnection == null) {
open();
}
// sends data
final HttpResponse httpResponse;
try {
final OutputStream outputStream = httpConnection.getOutputStream();
sendTo(outputStream);
final InputStream inputStream = httpConnection.getInputStream();
httpResponse = HttpResponse.readFrom(inputStream);
httpResponse.assignHttpRequest(this);
} catch (final IOException ioex) {
throw new HttpException(ioex);
}
final boolean keepAlive = httpResponse.isConnectionPersistent();
if (!keepAlive) {
// closes connection if keep alive is false, or if counter reached 0
httpConnection.close();
httpConnection = null;
}
return httpResponse;
}
open
设置httpRequest的httpConnectionProvider和httpConnection
// 一开始并没有httpConnectionProvider
public HttpRequest open() {
if (httpConnectionProvider == null) {
return open(HttpConnectionProvider.get());
}
return open(httpConnectionProvider);
}
public HttpRequest open(final HttpConnectionProvider httpConnectionProvider) {
if (this.httpConnection != null) {
throw new HttpException("Connection already opened");
}
try {
this.httpConnectionProvider = httpConnectionProvider;
this.httpConnection = httpConnectionProvider.createHttpConnection(this);
} catch (final IOException ioex) {
throw new HttpException("Can't connect to: " + url(), ioex);
}
return this;
}
HttpConnectionProvider
实际上是创建了一个SocketHttpConnectionProvider
public interface HttpConnectionProvider {
class Implementation {
private static HttpConnectionProvider httpConnectionProvider = new SocketHttpConnectionProvider();
public static void set(final HttpConnectionProvider httpConnectionProvider) {
Implementation.httpConnectionProvider = httpConnectionProvider;
}
}
// 返回一个SocketHttpConnectionProvider
static HttpConnectionProvider get() {
return Implementation.httpConnectionProvider;
}
public HttpConnection createHttpConnection(HttpRequest httpRequest) throws IOException;
}
createHttpConnection
开始创建连接,只保留核心流程:
@Override
public HttpConnection createHttpConnection(final HttpRequest httpRequest) throws IOException {
final SocketHttpConnection httpConnection;
// 创建一个socket
Socket socket = createSocket(httpRequest.host(), httpRequest.port(), httpRequest.connectionTimeout());
// 创建一个连接并返回
httpConnection = new SocketHttpConnection(socket);
return httpConnection;
}
createSocket
只保留核心流程,实际上是创建并开启一个socket,核心参数是host和port
protected Socket createSocket(final String host, final int port, final int connectionTimeout) throws IOException {
final SocketFactory socketFactory = resolveSocketFactory(proxy, false, false, connectionTimeout);
// 利用工厂创建一个socket并开启
if (connectionTimeout < 0) {
return socketFactory.createSocket(host, port);
}
// 设置超时时间
else {
Socket socket = socketFactory.createSocket();
socket.connect(new InetSocketAddress(host, port), connectionTimeout);
return socket;
}
}
// 默认情况下返回SocketFactory.getDefault()
protected ProxyInfo proxy = ProxyInfo.directProxy();
public static ProxyInfo directProxy() {
return new ProxyInfo(ProxyType.NONE, null, 0, null, null);
}
protected SocketFactory resolveSocketFactory(
final ProxyInfo proxy,
final boolean ssl,
final boolean trustAllCertificates,
final int connectionTimeout) throws IOException {
switch (proxy.getProxyType()) {
case NONE:
if (ssl) {
return getDefaultSSLSocketFactory(trustAllCertificates);
}
else {
return SocketFactory.getDefault();
}
}
}
HttpConnection
httpConnection = new SocketHttpConnection(socket),可以发现httpConnection实际上是socket的一个包装类,对外暴露了一些控制方法
public class SocketHttpConnection implements HttpConnection {
protected final Socket socket;
public SocketHttpConnection(final Socket socket) {
this.socket = socket;
}
@Override
public void init() throws IOException {
if (timeout >= 0) {
socket.setSoTimeout(timeout);
}
}
@Override
public OutputStream getOutputStream() throws IOException {
return socket.getOutputStream();
}
@Override
public InputStream getInputStream() throws IOException {
return socket.getInputStream();
}
@Override
public void close() {
try {
socket.close();
} catch (Throwable ignore) {
}
}
@Override
public void setTimeout(final int milliseconds) {
this.timeout = milliseconds;
}
public Socket getSocket() {
return socket;
}
private int timeout;
}
_send
回到_send,从httpConnection也就是socket控制器中拿到outputStream和inputStream,用于获取输入/输出
private HttpResponse _send() {
if (httpConnection == null) {
open();
}
// sends data
final HttpResponse httpResponse;
try {
// 从socket控制器中拿到outputStream
final OutputStream outputStream = httpConnection.getOutputStream();
// 向outputStream中写入内容
sendTo(outputStream);
// 从socket控制器中拿到inputStream
final InputStream inputStream = httpConnection.getInputStream();
// 拿到httpResponse
httpResponse = HttpResponse.readFrom(inputStream);
// httpResponse.httpRequest = httpRequest
httpResponse.assignHttpRequest(this);
} catch (final IOException ioex) {
throw new HttpException(ioex);
}
final boolean keepAlive = httpResponse.isConnectionPersistent();
// 如果不是长连接则关闭socket连接
if (!keepAlive) {
// closes connection if keep alive is false, or if counter reached 0
httpConnection.close();
httpConnection = null;
}
return httpResponse;
}
sendTo
先写buffer,再将buffer写入outputStream并发送请求
public void sendTo(final OutputStream out) throws IOException {
// 创建一个buffer
final Buffer buffer = buffer(true);
// 先把buffer中写入outputStream
if (httpProgressListener == null) {
buffer.writeTo(out);
}
else {
buffer.writeTo(out, httpProgressListener);
}
// 利用outputStream发送请求数据
out.flush();
}
先构造请求行,再通过populateHeaderAndBody构造请求头和请求体,最后生成完整的requestBuffer
@Override
protected Buffer buffer(final boolean fullRequest) {
// INITIALIZATION
// host port
if (header(HEADER_HOST) == null) {
setHostHeader();
}
// 请求体中的formBuffer
final Buffer formBuffer = formBuffer();
// query string
final String queryString = queryString();
// POST method requires Content-Type to be set
if (method.equals("POST") && (contentLength() == null)) {
contentLength(0);
}
// 完整的requestBuffer
final Buffer request = new Buffer();
// 设置请求行
request.append(method)
.append(SPACE)
.append(path);
if (query != null && !query.isEmpty()) {
request.append('?');
request.append(queryString);
}
request.append(SPACE)
.append(httpVersion)
.append(CRLF);
// 将formBuffer合并到requestBuffer
populateHeaderAndBody(request, formBuffer, fullRequest);
return request;
}
protected void populateHeaderAndBody(final Buffer target, final Buffer formBuffer, final boolean fullRequest) {
for (final String name : headers.names()) {
final List<String> values = headers.getAll(name);
final String key = capitalizeHeaderKeys ? HttpUtil.prepareHeaderParameterName(name) : name;
target.append(key);
target.append(": ");
int count = 0;
// 设置请求头
for (final String value : values) {
if (count++ > 0) {
target.append(", ");
}
target.append(value);
}
target.append(CRLF);
}
// 设置请求体
if (fullRequest) {
target.append(CRLF);
if (form != null) {
target.append(formBuffer);
} else if (body != null) {
target.append(body);
}
}
}
Buffer
Buffer是一种特殊的链表每次我们的写入都是操作链表末尾的缓冲区,writeTo将会buffer中的内容复制到outputStream中,先写buffer再写outputStream减少了网络IO
public class Buffer {
// 链表结构
protected LinkedList<Object> list = new LinkedList<>();
// 链表末尾的缓冲区
protected ByteArrayOutputStream last;
// 链表长度
protected int size;
// 每次写入都是操作链表末尾的缓冲区
public Buffer append(final String string) {
ensureLast();
final byte[] bytes = string.getBytes(StandardCharsets.ISO_8859_1);
last.write(bytes, 0, bytes.length);
size += bytes.length;
return this;
}
private void ensureLast() {
if (last == null) {
last = new ByteArrayOutputStream();
list.add(last);
}
}
// 将buffer中的内容复制到outputStream中
public void writeTo(final OutputStream out) throws IOException {
for (final Object o : list) {
if (o instanceof ByteArrayOutputStream) {
final ByteArrayOutputStream arrays = (ByteArrayOutputStream) o;
out.write(arrays.toByteArray());
}
else if (o instanceof Uploadable) {
final Uploadable uploadable = (Uploadable) o;
final InputStream inputStream = uploadable.openInputStream();
try {
IOUtil.copy(inputStream, out);
}
finally {
IOUtil.close(inputStream);
}
}
}
}
}
回到_send,看看拿到httpResponse的流程
private HttpResponse _send() {
if (httpConnection == null) {
open();
}
// sends data
final HttpResponse httpResponse;
try {
// 从socket控制器中拿到outputStream
final OutputStream outputStream = httpConnection.getOutputStream();
// 向outputStream中写入内容
sendTo(outputStream);
// 从socket控制器中拿到inputStream
final InputStream inputStream = httpConnection.getInputStream();
// 拿到httpResponse
httpResponse = HttpResponse.readFrom(inputStream);
// httpResponse.httpRequest = httpRequest
httpResponse.assignHttpRequest(this);
} catch (final IOException ioex) {
throw new HttpException(ioex);
}
final boolean keepAlive = httpResponse.isConnectionPersistent();
// 如果不是长连接则关闭socket连接
if (!keepAlive) {
// closes connection if keep alive is false, or if counter reached 0
httpConnection.close();
httpConnection = null;
}
return httpResponse;
}
readFrom
httpResponse = HttpResponse.readFrom(inputStream),只保留核心流程,实际上就是通过读取socket连接的inputStreamReader,创建httpResponse并设置状态码,状态短语,响应头,响应体
public static HttpResponse readFrom(final InputStream in) {
final InputStreamReader inputStreamReader = new InputStreamReader(in, StandardCharsets.ISO_8859_1);
final BufferedReader reader = new BufferedReader(inputStreamReader);
final HttpResponse httpResponse = new HttpResponse();
// the first line
String line;
try {
line = reader.readLine();
} catch (final IOException ioex) {
throw new HttpException(ioex);
}
// 设置状态码
try {
httpResponse.statusCode(Integer.parseInt(line.substring(ndx, ndx2).trim()));
}
catch (final NumberFormatException nfex) {
httpResponse.statusCode(-1);
}
// 设置状态短语
httpResponse.statusPhrase(line.substring(ndx2).trim());
}
// 设置响应头
httpResponse.readHeaders(reader);
// 设置响应体
httpResponse.readBody(reader);
return httpResponse;
}
至此已经成功的获取到了httpResponse
bodyText
返回httpResponse的body
public String bodyText() {
if (body == null) {
return StringPool.EMPTY;
}
if (charset != null) {
return StringUtil.convertCharset(body, StandardCharsets.ISO_8859_1, Charset.forName(charset));
}
return bodyRaw();
}
public String bodyRaw() {
return body;
}
例子2
写一个简单的session
HttpRequest httpRequest = HttpRequest.get("http://baidu.com");
HttpSession httpSession = new HttpSession();
httpSession.sendRequest(httpRequest);
String page = httpSession.getPage();
System.out.println(page);
源码分析2
HttpSession
创建了httpConnectionProvider,维护全局的cookies和heards,默认处理http传输过程中的异常和重定向
public class HttpSession {
protected HttpConnectionProvider httpConnectionProvider;
protected HttpRequest httpRequest;
protected HttpResponse httpResponse;
protected HttpMultiMap<Cookie> cookies = HttpMultiMap.newCaseInsensitiveMap();
protected HeadersMultiMap defaultHeaders = new HeadersMultiMap();
protected boolean keepAlive;
// 上一次发送httpRequest到返回httpResponse所用的时间
protected long elapsedTime;
// 是否处理http传输过程中的异常
protected boolean catchTransportExceptions = true;
// 是否处理重定向
protected boolean handleRedirects = true;
public HttpSession() {
httpConnectionProvider = HttpConnectionProvider.get();
}
}
sendRequest
只保留核心流程,发现session是通过维护全局的heards和cookies,保存多次请求和响应的信息实现的
public HttpResponse sendRequest(HttpRequest httpRequest) {
elapsedTime = System.currentTimeMillis();
while (true) {
this.httpRequest = httpRequest;
final HttpResponse previousResponse = this.httpResponse;
this.httpResponse = null;
// 读取httpSession,为httpRequest设置最新的headers和cookies
addDefaultHeaders(httpRequest);
addCookies(httpRequest);
// send request
if (catchTransportExceptions) {
try {
this.httpResponse = _sendRequest(httpRequest, previousResponse);
}
catch (final HttpException httpException) {
// 处理http传输过程中的异常
}
}
else {
this.httpResponse =_sendRequest(httpRequest, previousResponse);
}
// 读取httpResponse,为httpSession设置最新的headers和cookies
readCookies(httpResponse);
// 如果不需要处理重定向则直接退出
if (!handleRedirects) {
break;
}
final int statusCode = httpResponse.statusCode();
// 处理重定向的情况
// 301: moved permanently
if (statusCode == 301) {
final String newPath = httpResponse.location();
if (newPath == null) {
break;
}
httpRequest = HttpRequest.get(newPath);
continue;
}
// 302: redirect, 303: see other
if (statusCode == 302 || statusCode == 303) {
final String newPath = httpResponse.location();
if (newPath == null) {
break;
}
httpRequest = HttpRequest.get(newPath);
continue;
}
// 307: temporary redirect, 308: permanent redirect
if (statusCode == 307 || statusCode == 308) {
final String newPath = httpResponse.location();
if (newPath == null) {
break;
}
final String originalMethod = httpRequest.method();
final String originalBody = httpRequest.bodyRaw();
httpRequest = new HttpRequest()
.method(originalMethod)
.set(newPath)
.body(originalBody);
continue;
}
break;
}
elapsedTime = System.currentTimeMillis() - elapsedTime;
return this.httpResponse;
}
addDefaultHeaders
将httpSession的defaultHeaders中存在而httpRequest.headers中不存在的内容添加到httpRequest.headers中
protected void addDefaultHeaders(final HttpRequest httpRequest) {
for (final Map.Entry<String, String> entry : defaultHeaders.entries()) {
final String name = entry.getKey();
if (!httpRequest.headers.contains(name)) {
httpRequest.headers.add(name, entry.getValue());
}
}
}
addCookies
先将httpRequest中的cookies信息更新到httpSession中,再令当前httpRequest的cookies信息与httpSession中的cookies信息保持一致
protected void addCookies(final HttpRequest httpRequest) {
final List<Cookie> cookiesList = new ArrayList<>();
for (final Cookie cookie : httpRequest.cookies()) {
cookies.set(cookie.getName(),cookie);
}
if (!cookies.isEmpty()) {
for (final Map.Entry<String, Cookie> cookieEntry : cookies) {
cookiesList.add(cookieEntry.getValue());
}
httpRequest.cookies(cookiesList.toArray(new Cookie[0]));
}
}
// 从headers的cookie中拿出对应信息
public Cookie[] cookies() {
final String cookieHeader = header("cookie");
if (!StringUtil.isNotBlank(cookieHeader)) {
return new Cookie[0];
}
return Arrays
.stream(StringUtil.splitc(cookieHeader, ';'))
.map(Cookie::new)
.toArray(Cookie[]::new);
}
// 将有效的cookies设置到headers中
public HttpRequest cookies(final Cookie... cookies) {
if (cookies.length == 0) {
return this;
}
final StringBuilder cookieString = new StringBuilder();
boolean first = true;
for (final Cookie cookie : cookies) {
final Integer maxAge = cookie.getMaxAge();
if (maxAge != null && maxAge == 0) {
continue;
}
if (!first) {
cookieString.append("; ");
}
first = false;
cookieString.append(cookie.getName());
cookieString.append('=');
cookieString.append(cookie.getValue());
}
headerOverwrite("cookie", cookieString.toString());
return this;
}
_sendRequest
默认情况下会走例子1中open和send的流程,拿到httpResponse
protected HttpResponse _sendRequest(final HttpRequest httpRequest, final HttpResponse previousResponse) {
// 默认情况下keepAlive为false
if (!keepAlive) {
// 打开httpConnection
httpRequest.open(httpConnectionProvider);
} else {
// keeping alive
if (previousResponse == null) {
httpRequest.open(httpConnectionProvider).connectionKeepAlive(true);
} else {
httpRequest.keepAlive(previousResponse, true);
}
}
// 发送httpRequest返回httpResponse
return httpRequest.send();
}
readCookies
将httpResponse中的cookies信息更新到httpSession中
protected void readCookies(final HttpResponse httpResponse) {
final Cookie[] newCookies = httpResponse.cookies();
for (final Cookie cookie : newCookies) {
cookies.set(cookie.getName(), cookie);
}
}
public Cookie[] cookies() {
final List<String> newCookies = headers("set-cookie");
if (newCookies == null) {
return new Cookie[0];
}
final List<Cookie> cookieList = new ArrayList<>(newCookies.size());
for (final String cookieValue : newCookies) {
try {
final Cookie cookie = new Cookie(cookieValue);
cookieList.add(cookie);
}
catch (final Exception ex) {
// ignore
}
}
return cookieList.toArray(new Cookie[0]);
}
getPage
返回httpResponse的body
public String getPage() {
if (httpResponse == null) {
return null;
}
return httpResponse.bodyText();
}