org.voovan.http.client.HttpClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of voovan-web Show documentation
Show all versions of voovan-web Show documentation
Voovan is a java framwork and it not depends on any third-party framework.
package org.voovan.http.client;
import org.voovan.http.HttpSessionParam;
import org.voovan.http.message.Request;
import org.voovan.http.message.Response;
import org.voovan.http.message.packet.Cookie;
import org.voovan.http.message.packet.Header;
import org.voovan.http.message.packet.Part;
import org.voovan.http.server.HttpRequest;
import org.voovan.http.HttpRequestType;
import org.voovan.http.websocket.WebSocketFrame;
import org.voovan.http.websocket.WebSocketRouter;
import org.voovan.http.websocket.WebSocketSession;
import org.voovan.http.websocket.WebSocketType;
import org.voovan.http.websocket.exception.WebSocketFilterException;
import org.voovan.network.IoSession;
import org.voovan.network.SSLManager;
import org.voovan.network.aio.AioSocket;
import org.voovan.network.exception.ReadMessageException;
import org.voovan.network.exception.SendMessageException;
import org.voovan.network.messagesplitter.HttpMessageSplitter;
import org.voovan.tools.TEnv;
import org.voovan.tools.TObject;
import org.voovan.tools.TString;
import org.voovan.tools.log.Logger;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeoutException;
/**
* HTTP 请求调用
* @author helyho
*
* Voovan Framework.
* WebSite: https://github.com/helyho/Voovan
* Licence: Apache v2 License
*/
public class HttpClient implements Closeable{
private AioSocket socket;
private HttpRequest httpRequest;
private Map parameters;
private String charset="UTF-8";
private String urlString;
private boolean isSSL = false;
private boolean isWebSocket = false;
private WebSocketRouter webSocketRouter;
/**
* 构建函数
* @param urlString 请求的 URL 地址
*/
public HttpClient(String urlString) {
this.urlString = urlString;
init(urlString,5);
}
/**
* 构建函数
* @param urlString 请求的 URL 地址
* @param timeOut 超时时间
*/
public HttpClient(String urlString,int timeOut) {
this.urlString = urlString;
init(urlString,timeOut);
}
/**
* 构建函数
* @param urlString 请求的 URL 地址
* @param charset 字符集
* @param timeOut 超时时间
*/
public HttpClient(String urlString,String charset,int timeOut) {
this.urlString = urlString;
this.charset = charset;
init(urlString,timeOut);
}
/**
* 构建函数
* @param urlString 请求的 URL 地址
* @param charset 字符集
*/
public HttpClient(String urlString,String charset) {
this.urlString = urlString;
this.charset = charset;
init(urlString,5);
}
private boolean trySSL(String urlString){
boolean isSSL = urlString.toLowerCase().startsWith("https://");
if(!isSSL){
isSSL = urlString.toLowerCase().startsWith("wss://");
}
return isSSL;
}
/**
* 初始化函数
* @param urlString 主机地址
* @param timeOut 超时时间
*/
private void init(String urlString,int timeOut){
try {
isSSL = trySSL(urlString);
String hostString = urlString;
int port = 80;
if(hostString.toLowerCase().startsWith("ws")){
hostString = "http"+hostString.substring(2,hostString.length());
}
if(hostString.toLowerCase().startsWith("http")){
URL url = new URL(hostString);
hostString = url.getHost();
port = url.getPort();
}
if(port==-1 && !isSSL){
port = 80;
}else if(port==-1 && isSSL){
port = 443;
}
parameters = new HashMap();
socket = new AioSocket(hostString, port==-1?80:port, timeOut*1000);
socket.filterChain().add(new HttpClientFilter(this));
socket.messageSplitter(new HttpMessageSplitter());
if(isSSL){
try {
SSLManager sslManager = new SSLManager("TLS");
socket.setSSLManager(sslManager);
} catch (NoSuchAlgorithmException e) {
Logger.error(e);
}
}
socket.syncStart();
httpRequest = new HttpRequest(new Request(), this.charset, socket.getSession());
//初始化请求参数,默认值
httpRequest.header().put("Host", hostString);
httpRequest.header().put("Pragma", "no-cache");
httpRequest.header().put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
httpRequest.header().put("User-Agent", "Voovan Http Client");
httpRequest.header().put("Accept-Encoding","gzip");
httpRequest.header().put("Connection","keep-alive");
} catch (IOException e) {
Logger.error("HttpClient init error",e);
}
}
/**
* 获取 Socket 连接
* @return Socket对象
*/
protected AioSocket getSocket(){
return socket;
}
/**
* 读取流
* @return 字节缓冲对象ByteBuffer
* @throws IOException IO异常对象
*/
public ByteBuffer loadStream() throws IOException {
IoSession session = socket.getSession();
ByteBuffer tmpBuffer = ByteBuffer.allocate(socket.getReadBufferSize());
session.enabledMessageSpliter(false);
int readSize = session.read(tmpBuffer);
if(session.getAttribute("SocketException") instanceof Exception){
session.close();
return null;
}else if(readSize > 0) {
return tmpBuffer;
} else if(readSize == 0){
tmpBuffer.flip();
}else if(readSize == -1){
return null;
}
return tmpBuffer;
}
/**
* 设置请求方法
* @param method Http 请求的方法
* @return HttpClient 对象
*/
public HttpClient setMethod(String method){
httpRequest.protocol().setMethod(method);
return this;
}
/**
* 设置报文形式
* @param bodyType Http 报文形式
* @return Request.BodyType 枚举
*/
public HttpClient setBodyType(Request.RequestType bodyType){
//如果之前设置过 ContentType 则不自动设置 ContentType
if(!httpRequest.header().contain("Content-Type")) {
if (bodyType == Request.RequestType.BODY_MULTIPART) {
httpRequest.header().put("Content-Type", "multipart/form-data;");
} else if (bodyType == Request.RequestType.BODY_URLENCODED) {
httpRequest.header().put("Content-Type", "application/x-www-form-urlencoded");
}
}
return this;
}
/**
* 设置请求内容
* @param data 请求内容
* @return HttpClient 对象
*/
public HttpClient setData(byte[] data){
if(data!=null) {
httpRequest.body().write(data);
}
return this;
}
/**
* 设置请求内容
* @param data 请求内容
* @return HttpClient 对象
*/
public HttpClient setData(String data){
if(data!=null) {
httpRequest.body().write(data);
}
return this;
}
/**
* 设置请求内容
* @param data 请求内容
* @param charset 字符集
* @return HttpClient 对象
*/
public HttpClient setData(String data, String charset){
if(data!=null) {
httpRequest.body().write(data, charset);
}
return this;
}
/**
* 获取请求头集合
* @return Header 对象
*/
public Header getHeader(){
return httpRequest.header();
}
/**
* 设置请求头
* @param name Header 名称
* @param value Header 值
* @return HttpClient 对象
*/
public HttpClient putHeader(String name ,String value){
httpRequest.header().put(name, value);
return this;
}
/**
* 获取Cookie集合
* @return Cookie集合
*/
public List getCookies(){
return httpRequest.cookies();
}
/**
* 获取请求参数集合
* @return 参数对象
*/
public Map getParameters(){
return parameters;
}
/**
* 设置请求参数
* @param name 参数名
* @param value 参数值
* @return HttpClient 对象
*/
public HttpClient putParameters(String name, String value){
parameters.put(name, value);
return this;
}
/**
* 设置POST多段请求
* 类似 Form 的 Actiong="POST" enctype="multipart/form-data"
* @param part POST 请求包文对象
* @return HttpClient 对象
*/
public HttpClient addPart(Part part){
httpRequest.parts().add(part);
return this;
}
/**
* 上传文件
* @param name 参数名
* @param file 文件对象
*/
public void uploadFile(String name, File file){
setBodyType(Request.RequestType.BODY_MULTIPART);
parameters.put(name, file);
}
/**
* 构建QueryString
* 将 Map 集合转换成 QueryString 字符串
* @param parameters 用于保存拼装请求字符串参数的 Map 对象
* @param charset 参数的字符集
* @return 请求字符串
*/
public static String buildQueryString(Map parameters,String charset){
String queryString = "";
StringBuilder queryStringBuilder = new StringBuilder();
try {
for (Entry parameter : parameters.entrySet()) {
queryStringBuilder.append(parameter.getKey());
queryStringBuilder.append("=");
queryStringBuilder.append(URLEncoder.encode(TObject.nullDefault(parameter.getValue(),"").toString(), charset));
queryStringBuilder.append("&");
}
queryString = queryStringBuilder.toString();
queryString = queryStringBuilder.length()>0? TString.removeSuffix(queryString):queryString;
} catch (IOException e) {
Logger.error("HttpClient getQueryString error",e);
}
return queryString.isEmpty()? "" : queryString;
}
/**
* 构建QueryString
* 将 Map 集合转换成 QueryString 字符串
* @return 请求字符串
*/
private String getQueryString(){
return buildQueryString(parameters,charset);
}
/**
* 构建请求
*/
private void buildRequest(String urlString){
httpRequest.protocol().setPath(urlString.isEmpty()?"/":urlString);
//1.没有报文 Body,参数包含于请求URL
if (httpRequest.getBodyType() == Request.RequestType.NORMAL) {
String queryString = getQueryString();
if(!TString.isNullOrEmpty(queryString)) {
String requestPath = httpRequest.protocol().getPath();
if (requestPath.contains("?")) {
queryString = "&" + queryString;
} else {
queryString = "?" + queryString;
}
httpRequest.protocol().setPath(httpRequest.protocol().getPath() + queryString);
}
}
//2.请求报文Body 使用Part 类型
else if(httpRequest.getBodyType() == Request.RequestType.BODY_MULTIPART){
try{
for (Entry parameter : parameters.entrySet()) {
Part part = new Part();
part.header().put("name", parameter.getKey());
if(parameter.getValue() instanceof String) {
part.body().changeToBytes(URLEncoder.encode(parameter.getValue().toString(), charset).getBytes(charset));
}else if(parameter.getValue() instanceof File){
File file = (File) parameter.getValue();
//参数类型如果是文件则默认采用文件的形式
part.body().changeToFile(file);
part.header().put("filename", file.getName());
}
httpRequest.parts().add(part);
}
} catch (IOException e) {
Logger.error("HttpClient buildRequest error",e);
}
}
//3.请求报文Body 使用流类型
else if(httpRequest.getBodyType() == Request.RequestType.BODY_URLENCODED){
String queryString = getQueryString();
httpRequest.body().write(queryString, charset);
}
}
/**
* 连接并发送请求
* @param location 请求 URL
* @return Response 对象
* @throws SendMessageException 发送异常
* @throws ReadMessageException 读取异常
*/
public synchronized Response send(String location) throws SendMessageException, ReadMessageException {
if(isWebSocket){
throw new SendMessageException("The WebSocket is connect, you can't send http request.");
}
//设置默认的报文 Body 类型
if(httpRequest.protocol().getMethod().equals("POST") && httpRequest.parts().size()>0){
setBodyType(Request.RequestType.BODY_MULTIPART);
}else if(httpRequest.protocol().getMethod().equals("POST")) {
setBodyType(Request.RequestType.BODY_URLENCODED);
}else{
setBodyType(Request.RequestType.NORMAL);
}
//构造 Request 对象
buildRequest(TString.isNullOrEmpty(location)?"/":location);
//发送报文
try {
httpRequest.send(socket.getSession());
httpRequest.flush();
}catch(IOException e){
throw new SendMessageException("HttpClient send error",e);
}
try {
Object readObject = socket.synchronouRead();
Response response = null;
//如果是异常则抛出异常
if (readObject instanceof Exception) {
throw new ReadMessageException((Exception) readObject);
} else {
response = (Response) readObject;
}
//结束操作
finished(httpRequest, response);
return response;
}catch(ReadMessageException e){
if(!isWebSocket){
throw e;
}
}
return null;
}
/**
* 连接并发送请求
* @return Response 对象
* @throws SendMessageException 发送异常
* @throws ReadMessageException 读取异常
*/
public synchronized Response send() throws SendMessageException, ReadMessageException {
return send("/");
}
/**
* 发送二进制数据
* @param buffer 二进制数据
* @return 发送的字节数
* @throws IOException IO 异常
*/
public int send(ByteBuffer buffer) throws IOException {
return this.httpRequest.send(buffer);
}
/**
* 请求完成
* @param response 请求对象
*/
private void finished(Request request, Response response){
//传递 cookie 到 Request 对象
if(response!=null
&& response.cookies()!=null
&& !response.cookies().isEmpty()){
request.cookies().addAll(response.cookies());
}
request.body().changeToBytes(new byte[0]);
//清理请求对象,以便下次请求使用
parameters.clear();
request.body().clear();
request.parts().clear();
request.header().remove("Content-Type");
request.header().remove("Content-Length");
}
/**
* 升级协议
* @param location URL地址
* @return true: 升级成功, false: 升级失败
* @throws SendMessageException 发送消息异常
* @throws ReadMessageException 读取消息异常
*/
private void doWebSocketUpgrade(String location) throws SendMessageException, ReadMessageException {
IoSession session = socket.getSession();
session.removeAttribute(HttpSessionParam.TYPE);
httpRequest.header().put("Connection","Upgrade");
httpRequest.header().put("Upgrade", "websocket");
httpRequest.header().put("Pragma","no-cache");
httpRequest.header().put("Origin", this.urlString);
httpRequest.header().put("Sec-WebSocket-Version","13");
httpRequest.header().put("Sec-WebSocket-Key","c1Mm+c0b28erlzCWWYfrIg==");
send(location);
}
/**
* 连接 Websocket
* @param location URL地址
* @param webSocketRouter WebSocker的路由
* @throws SendMessageException 发送异常
* @throws ReadMessageException 读取异常
*/
public void webSocket(String location, WebSocketRouter webSocketRouter) throws SendMessageException, ReadMessageException {
this.webSocketRouter = webSocketRouter;
//处理升级后的消息
doWebSocketUpgrade(location);
//为异步调用进行阻赛,等待 socket 关闭
try {
TEnv.wait(socket.getReadTimeout(), ()->socket.isOpen());
} catch (TimeoutException e) {
e.printStackTrace();
}
}
/**
* 初始化 WebSocket
* 在 HttpFilter 中触发
*/
protected void initWebSocket( ){
//设置 WebSocket 标记
isWebSocket = true;
IoSession session = socket.getSession();
WebSocketSession webSocketSession = new WebSocketSession(socket.getSession(), webSocketRouter, WebSocketType.CLIENT);
WebSocketHandler webSocketHandler = new WebSocketHandler(this, webSocketSession, webSocketRouter);
webSocketSession.setWebSocketRouter(webSocketRouter);
//先注册Socket业务处理句柄,再打开消息分割器中 WebSocket 开关
socket.handler(webSocketHandler);
session.setAttribute(HttpSessionParam.TYPE, HttpRequestType.WEBSOCKET);
Object result = null;
//触发onOpen事件
result = webSocketRouter.onOpen(webSocketSession);
if(result!=null) {
//封包
ByteBuffer buffer = null;
try {
buffer = (ByteBuffer) webSocketRouter.filterEncoder(webSocketSession, result);
WebSocketFrame webSocketFrame = WebSocketFrame.newInstance(true, WebSocketFrame.Opcode.TEXT, true, buffer);
sendWebSocketData(webSocketFrame);
} catch (WebSocketFilterException e) {
Logger.error(e);
} catch (SendMessageException e) {
e.printStackTrace();
}
}
}
/**
* 发送 WebSocket 帧
* @param webSocketFrame WebSocket 帧
* @throws SendMessageException 发送异常
*/
private void sendWebSocketData(WebSocketFrame webSocketFrame) throws SendMessageException {
socket.getSession().syncSend(webSocketFrame);
}
/**
* 关闭 HTTP 连接
*/
@Override
public void close(){
if(socket!=null) {
socket.close();
}
}
/**
* 判断是否处于连接状态
* @return 是否连接
*/
public boolean isConnect(){
if(socket!=null) {
return socket.isConnected();
} else {
return false;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy