mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-08-18 20:38:02 +08:00
add http server engine
This commit is contained in:
@@ -27,7 +27,7 @@ import org.dromara.hutool.http.client.Response;
|
||||
import org.dromara.hutool.http.client.engine.ClientEngine;
|
||||
import org.dromara.hutool.http.client.engine.ClientEngineFactory;
|
||||
import org.dromara.hutool.http.meta.Method;
|
||||
import org.dromara.hutool.http.server.SimpleServer;
|
||||
import org.dromara.hutool.http.server.engine.sun.SimpleServer;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -16,17 +16,13 @@
|
||||
|
||||
package org.dromara.hutool.http.multipart;
|
||||
|
||||
import org.dromara.hutool.core.io.file.FileUtil;
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.core.io.IoUtil;
|
||||
import org.dromara.hutool.core.io.file.FileNameUtil;
|
||||
import org.dromara.hutool.core.io.file.FileUtil;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* 上传的文件对象
|
||||
@@ -80,9 +76,9 @@ public class UploadFile {
|
||||
*
|
||||
* @param destPath 目标文件路径
|
||||
* @return 目标文件
|
||||
* @throws IOException IO异常
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public File write(final String destPath) throws IOException {
|
||||
public File write(final String destPath) throws IORuntimeException {
|
||||
if (data != null || tempFile != null) {
|
||||
return write(FileUtil.file(destPath));
|
||||
}
|
||||
@@ -95,9 +91,9 @@ public class UploadFile {
|
||||
*
|
||||
* @param destination 目标文件
|
||||
* @return 目标文件
|
||||
* @throws IOException IO异常
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public File write(File destination) throws IOException {
|
||||
public File write(File destination) throws IORuntimeException {
|
||||
assertValid();
|
||||
|
||||
if (destination.isDirectory()) {
|
||||
@@ -113,7 +109,7 @@ public class UploadFile {
|
||||
throw new NullPointerException("Temp file is null !");
|
||||
}
|
||||
if(!this.tempFile.exists()){
|
||||
throw new NoSuchFileException("Temp file: [" + this.tempFile.getAbsolutePath() + "] not exist!");
|
||||
throw new IORuntimeException("Temp file: [" + this.tempFile.getAbsolutePath() + "] not exist!");
|
||||
}
|
||||
|
||||
FileUtil.move(tempFile, destination, true);
|
||||
@@ -123,9 +119,9 @@ public class UploadFile {
|
||||
|
||||
/**
|
||||
* @return 获得文件字节流
|
||||
* @throws IOException IO异常
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public byte[] getFileContent() throws IOException {
|
||||
public byte[] getFileContent() throws IORuntimeException {
|
||||
assertValid();
|
||||
|
||||
if (data != null) {
|
||||
@@ -139,9 +135,9 @@ public class UploadFile {
|
||||
|
||||
/**
|
||||
* @return 获得文件流
|
||||
* @throws IOException IO异常
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public InputStream getFileInputStream() throws IOException {
|
||||
public InputStream getFileInputStream() throws IORuntimeException {
|
||||
assertValid();
|
||||
|
||||
if (data != null) {
|
||||
@@ -278,11 +274,11 @@ public class UploadFile {
|
||||
/**
|
||||
* 断言是否文件流可用
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
private void assertValid() throws IOException {
|
||||
private void assertValid() throws IORuntimeException {
|
||||
if (!isUploaded()) {
|
||||
throw new IOException(StrUtil.format("File [{}] upload fail", getFileName()));
|
||||
throw new IORuntimeException(StrUtil.format("File [{}] upload fail", getFileName()));
|
||||
}
|
||||
}
|
||||
// ---------------------------------------------------------------------------- Private method end
|
||||
|
||||
@@ -1,446 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server;
|
||||
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.core.io.IoUtil;
|
||||
import org.dromara.hutool.core.io.file.FileUtil;
|
||||
import org.dromara.hutool.core.net.url.UrlEncoder;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.core.util.ByteUtil;
|
||||
import org.dromara.hutool.core.util.ObjUtil;
|
||||
import org.dromara.hutool.http.meta.ContentType;
|
||||
import org.dromara.hutool.http.meta.HeaderName;
|
||||
import org.dromara.hutool.http.meta.HttpStatus;
|
||||
import com.sun.net.httpserver.Headers;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Http响应对象,用于写出数据到客户端
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public class HttpServerResponse extends HttpServerBase {
|
||||
|
||||
private Charset charset;
|
||||
/**
|
||||
* 是否已经发送了Http状态码,如果没有,提前写出状态码
|
||||
*/
|
||||
private boolean isSendCode;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param httpExchange {@link HttpExchange}
|
||||
*/
|
||||
public HttpServerResponse(final HttpExchange httpExchange) {
|
||||
super(httpExchange);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送HTTP状态码,Content-Length为0不定长度,会输出Transfer-encoding: chunked
|
||||
*
|
||||
* @param httpStatusCode HTTP状态码,见HttpStatus
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse send(final int httpStatusCode) {
|
||||
return send(httpStatusCode, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送成功状态码
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse sendOk() {
|
||||
return send(HttpStatus.HTTP_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送成功状态码
|
||||
*
|
||||
* @param bodyLength 响应体长度,默认0表示不定长度,会输出Transfer-encoding: chunked
|
||||
* @return this
|
||||
* @since 5.5.7
|
||||
*/
|
||||
public HttpServerResponse sendOk(final int bodyLength) {
|
||||
return send(HttpStatus.HTTP_OK, bodyLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送404错误页
|
||||
*
|
||||
* @param content 错误页页面内容,默认text/html类型
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse send404(final String content) {
|
||||
return sendError(HttpStatus.HTTP_NOT_FOUND, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送错误页
|
||||
*
|
||||
* @param errorCode HTTP错误状态码,见HttpStatus
|
||||
* @param content 错误页页面内容,默认text/html类型
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse sendError(final int errorCode, final String content) {
|
||||
send(errorCode);
|
||||
setContentType(ContentType.TEXT_HTML.toString());
|
||||
return write(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送HTTP状态码
|
||||
*
|
||||
* @param httpStatusCode HTTP状态码,见HttpStatus
|
||||
* @param bodyLength 响应体长度,默认0表示不定长度,会输出Transfer-encoding: chunked
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse send(final int httpStatusCode, final long bodyLength) {
|
||||
if (this.isSendCode) {
|
||||
throw new IORuntimeException("Http status code has been send!");
|
||||
}
|
||||
|
||||
try {
|
||||
this.httpExchange.sendResponseHeaders(httpStatusCode, bodyLength);
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
|
||||
this.isSendCode = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得所有响应头,获取后可以添加新的响应头
|
||||
*
|
||||
* @return 响应头
|
||||
*/
|
||||
public Headers getHeaders() {
|
||||
return this.httpExchange.getResponseHeaders();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加响应头,如果已经存在,则追加
|
||||
*
|
||||
* @param header 头key
|
||||
* @param value 值
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse addHeader(final String header, final String value) {
|
||||
getHeaders().add(header, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应头,如果已经存在,则覆盖
|
||||
*
|
||||
* @param headerName 头key
|
||||
* @param value 值
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse setHeader(final HeaderName headerName, final String value) {
|
||||
return setHeader(headerName.getValue(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应头,如果已经存在,则覆盖
|
||||
*
|
||||
* @param header 头key
|
||||
* @param value 值
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse setHeader(final String header, final String value) {
|
||||
getHeaders().set(header, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应头,如果已经存在,则覆盖
|
||||
*
|
||||
* @param header 头key
|
||||
* @param value 值列表
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse setHeader(final String header, final List<String> value) {
|
||||
getHeaders().put(header, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置所有响应头,如果已经存在,则覆盖
|
||||
*
|
||||
* @param headers 响应头map
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse setHeaders(final Map<String, List<String>> headers) {
|
||||
getHeaders().putAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Content-Type头,类似于:text/html;charset=utf-8<br>
|
||||
* 如果用户传入的信息无charset信息,自动根据charset补充,charset设置见{@link #setCharset(Charset)}
|
||||
*
|
||||
* @param contentType Content-Type头内容
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse setContentType(String contentType) {
|
||||
if (null != contentType && null != this.charset) {
|
||||
if (!contentType.contains(";charset=")) {
|
||||
contentType = ContentType.build(contentType, this.charset);
|
||||
}
|
||||
}
|
||||
|
||||
return setHeader(HeaderName.CONTENT_TYPE, contentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Content-Length头
|
||||
*
|
||||
* @param contentLength Content-Length头内容
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse setContentLength(final long contentLength) {
|
||||
return setHeader(HeaderName.CONTENT_LENGTH, String.valueOf(contentLength));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应的编码
|
||||
*
|
||||
* @param charset 编码
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse setCharset(final Charset charset) {
|
||||
this.charset = charset;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置属性
|
||||
*
|
||||
* @param name 属性名
|
||||
* @param value 属性值
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse setAttr(final String name, final Object value) {
|
||||
this.httpExchange.setAttribute(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应数据流
|
||||
*
|
||||
* @return 响应数据流
|
||||
*/
|
||||
public OutputStream getOut() {
|
||||
if (!this.isSendCode) {
|
||||
sendOk();
|
||||
}
|
||||
return this.httpExchange.getResponseBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应数据流
|
||||
*
|
||||
* @return 响应数据流
|
||||
*/
|
||||
public PrintWriter getWriter() {
|
||||
final Charset charset = ObjUtil.defaultIfNull(this.charset, DEFAULT_CHARSET);
|
||||
return new PrintWriter(new OutputStreamWriter(getOut(), charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数据到客户端
|
||||
*
|
||||
* @param data 数据
|
||||
* @param contentType Content-Type类型
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse write(final String data, final String contentType) {
|
||||
setContentType(contentType);
|
||||
return write(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数据到客户端
|
||||
*
|
||||
* @param data 数据
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse write(final String data) {
|
||||
final Charset charset = ObjUtil.defaultIfNull(this.charset, DEFAULT_CHARSET);
|
||||
return write(ByteUtil.toBytes(data, charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数据到客户端
|
||||
*
|
||||
* @param data 数据
|
||||
* @param contentType 返回的类型
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse write(final byte[] data, final String contentType) {
|
||||
setContentType(contentType);
|
||||
return write(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数据到客户端
|
||||
*
|
||||
* @param data 数据
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse write(final byte[] data) {
|
||||
final ByteArrayInputStream in = new ByteArrayInputStream(data);
|
||||
return write(in, in.available());
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数据给客户端
|
||||
*
|
||||
* @param in 需要返回客户端的内容
|
||||
* @param contentType 返回的类型
|
||||
* @return this
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public HttpServerResponse write(final InputStream in, final String contentType) {
|
||||
return write(in, 0, contentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数据给客户端
|
||||
*
|
||||
* @param in 需要返回客户端的内容
|
||||
* @param length 内容长度,默认0表示不定长度,会输出Transfer-encoding: chunked
|
||||
* @param contentType 返回的类型
|
||||
* @return this
|
||||
* @since 5.2.7
|
||||
*/
|
||||
public HttpServerResponse write(final InputStream in, final int length, final String contentType) {
|
||||
setContentType(contentType);
|
||||
return write(in, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数据到客户端
|
||||
*
|
||||
* @param in 数据流
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse write(final InputStream in) {
|
||||
return write(in, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数据到客户端
|
||||
*
|
||||
* @param in 数据流
|
||||
* @param length 指定响应内容长度,默认0表示不定长度,会输出Transfer-encoding: chunked
|
||||
* @return this
|
||||
*/
|
||||
public HttpServerResponse write(final InputStream in, final int length) {
|
||||
if (!isSendCode) {
|
||||
sendOk(Math.max(0, length));
|
||||
}
|
||||
OutputStream out = null;
|
||||
try {
|
||||
out = this.httpExchange.getResponseBody();
|
||||
IoUtil.copy(in, out);
|
||||
} finally {
|
||||
IoUtil.closeQuietly(out);
|
||||
IoUtil.closeQuietly(in);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回文件给客户端(文件下载)
|
||||
*
|
||||
* @param file 写出的文件对象
|
||||
* @return this
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public HttpServerResponse write(final File file) {
|
||||
return write(file, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回文件给客户端(文件下载)
|
||||
*
|
||||
* @param file 写出的文件对象
|
||||
* @param fileName 文件名
|
||||
* @return this
|
||||
* @since 5.5.8
|
||||
*/
|
||||
public HttpServerResponse write(final File file, String fileName) {
|
||||
final long fileSize = file.length();
|
||||
if(fileSize > Integer.MAX_VALUE){
|
||||
throw new IllegalArgumentException("File size is too bigger than " + Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
if(StrUtil.isBlank(fileName)){
|
||||
fileName = file.getName();
|
||||
}
|
||||
final String contentType = FileUtil.getMimeType(fileName, ContentType.OCTET_STREAM.getValue());
|
||||
BufferedInputStream in = null;
|
||||
try {
|
||||
in = FileUtil.getInputStream(file);
|
||||
write(in, (int)fileSize, contentType, fileName);
|
||||
} finally {
|
||||
IoUtil.closeQuietly(in);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回文件数据给客户端(文件下载)
|
||||
*
|
||||
* @param in 需要返回客户端的内容
|
||||
* @param contentType 返回的类型
|
||||
* @param fileName 文件名
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public void write(final InputStream in, final String contentType, final String fileName) {
|
||||
write(in, 0, contentType, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回文件数据给客户端(文件下载)
|
||||
*
|
||||
* @param in 需要返回客户端的内容
|
||||
* @param length 长度
|
||||
* @param contentType 返回的类型
|
||||
* @param fileName 文件名
|
||||
* @return this
|
||||
* @since 5.2.7
|
||||
*/
|
||||
public HttpServerResponse write(final InputStream in, final int length, final String contentType, final String fileName) {
|
||||
final Charset charset = ObjUtil.defaultIfNull(this.charset, DEFAULT_CHARSET);
|
||||
|
||||
if (!contentType.startsWith("text/")) {
|
||||
// 非文本类型数据直接走下载
|
||||
setHeader(HeaderName.CONTENT_DISPOSITION, StrUtil.format("attachment;filename={}", UrlEncoder.encodeAll(fileName, charset)));
|
||||
}
|
||||
return write(in, length, contentType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
/**
|
||||
* 服务器配置
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class ServerConfig {
|
||||
|
||||
/**
|
||||
* 创建配置
|
||||
*
|
||||
* @return 配置
|
||||
*/
|
||||
public static ServerConfig of(){
|
||||
return new ServerConfig();
|
||||
}
|
||||
|
||||
private String host = "localhost";
|
||||
private int port = 8888;
|
||||
private String root;
|
||||
private SSLContext sslContext;
|
||||
|
||||
/**
|
||||
* 获取服务器地址,默认127.0.0.1
|
||||
*
|
||||
* @return 服务器地址
|
||||
*/
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置服务器地址,默认127.0.0.1
|
||||
*
|
||||
* @param host 服务器地址
|
||||
* @return this
|
||||
*/
|
||||
public ServerConfig setHost(final String host) {
|
||||
this.host = host;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务器端口
|
||||
*
|
||||
* @return 服务器端口
|
||||
*/
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置服务器端口
|
||||
*
|
||||
* @param port 服务器端口
|
||||
* @return this
|
||||
*/
|
||||
public ServerConfig setPort(final int port) {
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务器根目录
|
||||
*
|
||||
* @return 服务器根目录
|
||||
*/
|
||||
public String getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置服务器根目录
|
||||
*
|
||||
* @param root 服务器根目录
|
||||
* @return this
|
||||
*/
|
||||
public ServerConfig setRoot(final String root) {
|
||||
this.root = root;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SSL上下文
|
||||
*
|
||||
* @return SSL上下文
|
||||
*/
|
||||
public SSLContext getSslContext() {
|
||||
return sslContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置SSL上下文
|
||||
*
|
||||
* @param sslContext SSL上下文
|
||||
* @return this
|
||||
*/
|
||||
public ServerConfig setSslContext(final SSLContext sslContext) {
|
||||
this.sslContext = sslContext;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server;
|
||||
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.core.io.IoUtil;
|
||||
import org.dromara.hutool.core.map.multi.ListValueMap;
|
||||
import org.dromara.hutool.core.net.url.UrlQueryUtil;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.core.util.CharsetUtil;
|
||||
import org.dromara.hutool.core.util.ObjUtil;
|
||||
import org.dromara.hutool.http.meta.ContentTypeUtil;
|
||||
import org.dromara.hutool.http.meta.HeaderName;
|
||||
import org.dromara.hutool.http.meta.Method;
|
||||
import org.dromara.hutool.http.multipart.MultipartFormData;
|
||||
import org.dromara.hutool.http.multipart.UploadSetting;
|
||||
import org.dromara.hutool.http.useragent.UserAgent;
|
||||
import org.dromara.hutool.http.useragent.UserAgentUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 服务端请求对象,用于获取请求参数等
|
||||
*
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public interface ServerRequest {
|
||||
|
||||
/**
|
||||
* 默认编码,用于获取请求头和响应头编码,默认为UTF-8
|
||||
*/
|
||||
Charset DEFAULT_CHARSET = CharsetUtil.UTF_8;
|
||||
|
||||
// region ----- method
|
||||
|
||||
/**
|
||||
* 获取请求方法
|
||||
*
|
||||
* @return 请求方法
|
||||
*/
|
||||
String getMethod();
|
||||
|
||||
/**
|
||||
* 是否为GET请求
|
||||
*
|
||||
* @return 是否为GET请求
|
||||
*/
|
||||
default boolean isGetMethod() {
|
||||
return Method.GET.name().equalsIgnoreCase(getMethod());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为POST请求
|
||||
*
|
||||
* @return 是否为POST请求
|
||||
*/
|
||||
default boolean isPostMethod() {
|
||||
return Method.POST.name().equalsIgnoreCase(getMethod());
|
||||
}
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* 获取请求路径,包含请求参数部分
|
||||
*
|
||||
* @return 请求路径,包含请求参数部分
|
||||
*/
|
||||
String getPath();
|
||||
|
||||
/**
|
||||
* 获取请求参数,包括pathVariable和queryString
|
||||
*
|
||||
* @return 请求参数,包括pathVariable和queryString
|
||||
*/
|
||||
String getQuery();
|
||||
|
||||
// region ----- header
|
||||
|
||||
/**
|
||||
* 获取请求头
|
||||
*
|
||||
* @param name 请求头名
|
||||
* @return 请求头
|
||||
*/
|
||||
String getHeader(final String name);
|
||||
|
||||
/**
|
||||
* 获得请求header中的信息
|
||||
*
|
||||
* @param headerNameKey 头信息的KEY
|
||||
* @return header值
|
||||
*/
|
||||
default String getHeader(final HeaderName headerNameKey) {
|
||||
return getHeader(headerNameKey.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得请求header中的信息
|
||||
*
|
||||
* @param headerKey 头信息的KEY
|
||||
* @param charset 字符集
|
||||
* @return header值
|
||||
*/
|
||||
default String getHeader(final String headerKey, final Charset charset) {
|
||||
final String header = getHeader(headerKey);
|
||||
if (null != header) {
|
||||
return CharsetUtil.convert(header, CharsetUtil.ISO_8859_1, charset);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Content-Type头信息
|
||||
*
|
||||
* @return Content-Type头信息
|
||||
*/
|
||||
default String getContentType() {
|
||||
return getHeader(HeaderName.CONTENT_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Content-Length头信息,单位:字节
|
||||
*
|
||||
* @return Content-Length头信息,单位:字节
|
||||
*/
|
||||
default long getContentLength() {
|
||||
final String contentLength = getHeader(HeaderName.CONTENT_LENGTH);
|
||||
return StrUtil.isEmpty(contentLength) ? -1 : Long.parseLong(contentLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取编码,获取失败默认使用UTF-8,获取规则如下:
|
||||
*
|
||||
* <pre>
|
||||
* 1、从Content-Type头中获取编码,类似于:text/html;charset=utf-8
|
||||
* </pre>
|
||||
*
|
||||
* @return 编码,默认UTF-8
|
||||
*/
|
||||
default Charset getCharset() {
|
||||
final String contentType = getContentType();
|
||||
return ObjUtil.defaultIfNull(ContentTypeUtil.getCharset(contentType), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得User-Agent
|
||||
*
|
||||
* @return User-Agent字符串
|
||||
*/
|
||||
default String getUserAgentStr() {
|
||||
return getHeader(HeaderName.USER_AGENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得User-Agent,未识别返回null
|
||||
*
|
||||
* @return User-Agent字符串,未识别返回null
|
||||
*/
|
||||
default UserAgent getUserAgent() {
|
||||
return UserAgentUtil.parse(getUserAgentStr());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得Cookie信息字符串
|
||||
*
|
||||
* @return cookie字符串
|
||||
*/
|
||||
default String getCookiesStr() {
|
||||
return getHeader(HeaderName.COOKIE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为Multipart类型表单,此类型表单用于文件上传
|
||||
*
|
||||
* @return 是否为Multipart类型表单,此类型表单用于文件上传
|
||||
*/
|
||||
default boolean isMultipart() {
|
||||
if (!isPostMethod()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final String contentType = getContentType();
|
||||
if (StrUtil.isBlank(contentType)) {
|
||||
return false;
|
||||
}
|
||||
return contentType.toLowerCase().startsWith("multipart/");
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region ----- body
|
||||
|
||||
/**
|
||||
* 获取请求体流
|
||||
*
|
||||
* @return 请求体流
|
||||
*/
|
||||
InputStream getBodyStream();
|
||||
|
||||
/**
|
||||
* 获取请求体文本,可以是form表单、json、xml等任意内容<br>
|
||||
* 使用{@link #getCharset()}判断编码,判断失败使用UTF-8编码
|
||||
*
|
||||
* @return 请求
|
||||
*/
|
||||
default String getBody() {
|
||||
return getBody(getCharset());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求体文本,可以是form表单、json、xml等任意内容
|
||||
*
|
||||
* @param charset 编码
|
||||
* @return 请求
|
||||
*/
|
||||
default String getBody(final Charset charset) {
|
||||
return StrUtil.str(getBodyBytes(), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取body的bytes数组
|
||||
*
|
||||
* @return body的bytes数组
|
||||
*/
|
||||
default byte[] getBodyBytes() {
|
||||
return IoUtil.readBytes(getBodyStream(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得MultiPart表单内容,多用于获得上传的文件
|
||||
*
|
||||
* @return MultipartFormData
|
||||
* @throws IORuntimeException IO异常
|
||||
* @since 5.3.0
|
||||
*/
|
||||
default MultipartFormData getMultipart() throws IORuntimeException {
|
||||
return parseMultipart(new UploadSetting());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得multipart/form-data 表单内容<br>
|
||||
* 包括文件和普通表单数据<br>
|
||||
* 在同一次请求中,此方法只能被执行一次!
|
||||
*
|
||||
* @param uploadSetting 上传文件的设定,包括最大文件大小、保存在内存的边界大小、临时目录、扩展名限定等
|
||||
* @return MultiPart表单
|
||||
* @throws IORuntimeException IO异常
|
||||
* @since 5.3.0
|
||||
*/
|
||||
default MultipartFormData parseMultipart(final UploadSetting uploadSetting) throws IORuntimeException {
|
||||
final MultipartFormData formData = new MultipartFormData(uploadSetting);
|
||||
try {
|
||||
formData.parseRequestStream(getBodyStream(), getCharset());
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
|
||||
return formData;
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region ----- param
|
||||
/**
|
||||
* 获取指定名称的参数值,取第一个值
|
||||
* @param name 参数名
|
||||
* @return 参数值
|
||||
* @since 5.5.8
|
||||
*/
|
||||
default String getParam(final String name){
|
||||
return getParams().getValue(name, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定名称的参数值
|
||||
*
|
||||
* @param name 参数名
|
||||
* @return 参数值
|
||||
* @since 5.5.8
|
||||
*/
|
||||
default Collection<String> getParams(final String name){
|
||||
return getParams().get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数Map
|
||||
*
|
||||
* @return 参数map
|
||||
*/
|
||||
default ListValueMap<String, String> getParams() {
|
||||
final ListValueMap<String, String> params = new ListValueMap<>();
|
||||
final Charset charset = getCharset();
|
||||
|
||||
//解析URL中的参数
|
||||
final String query = getQuery();
|
||||
if(StrUtil.isNotBlank(query)){
|
||||
params.putAll(UrlQueryUtil.decodeQueryList(query, charset));
|
||||
}
|
||||
|
||||
// 解析multipart中的参数
|
||||
if(isMultipart()){
|
||||
params.putAll(getMultipart().getParamListMap());
|
||||
} else{
|
||||
// 解析body中的参数
|
||||
final String body = getBody();
|
||||
if(StrUtil.isNotBlank(body)){
|
||||
params.putAll(UrlQueryUtil.decodeQueryList(body, charset));
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server;
|
||||
|
||||
import org.dromara.hutool.core.io.IoUtil;
|
||||
import org.dromara.hutool.core.io.file.FileUtil;
|
||||
import org.dromara.hutool.core.net.url.UrlEncoder;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.core.util.ByteUtil;
|
||||
import org.dromara.hutool.core.util.CharsetUtil;
|
||||
import org.dromara.hutool.core.util.ObjUtil;
|
||||
import org.dromara.hutool.http.meta.ContentType;
|
||||
import org.dromara.hutool.http.meta.HeaderName;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 服务端响应接口,用于写出数据
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public interface ServerResponse {
|
||||
|
||||
/**
|
||||
* 默认编码,用于获取请求头和响应头编码,默认为UTF-8
|
||||
*/
|
||||
Charset DEFAULT_CHARSET = CharsetUtil.UTF_8;
|
||||
|
||||
/**
|
||||
* 设置状态码
|
||||
*
|
||||
* @param statusCode 状态码
|
||||
* @return this
|
||||
*/
|
||||
ServerResponse setStatus(int statusCode);
|
||||
|
||||
/**
|
||||
* 设置编码,默认为UTF-8
|
||||
*
|
||||
* @param charset 编码
|
||||
* @return this
|
||||
*/
|
||||
ServerResponse setCharset(Charset charset);
|
||||
|
||||
/**
|
||||
* 获取编码,默认为UTF-8
|
||||
*
|
||||
* @return 编码
|
||||
*/
|
||||
Charset getCharset();
|
||||
|
||||
/**
|
||||
* 添加响应头,如果已经存在,则追加
|
||||
*
|
||||
* @param header 头key
|
||||
* @param value 值
|
||||
* @return this
|
||||
*/
|
||||
ServerResponse addHeader(final String header, final String value);
|
||||
|
||||
/**
|
||||
* 设置响应头,如果已经存在,则覆盖
|
||||
*
|
||||
* @param header 头key
|
||||
* @param value 值
|
||||
* @return this
|
||||
*/
|
||||
ServerResponse setHeader(final String header, final String value);
|
||||
|
||||
/**
|
||||
* 设置响应头,如果已经存在,则覆盖
|
||||
*
|
||||
* @param headerName 头key
|
||||
* @param value 值
|
||||
* @return this
|
||||
*/
|
||||
default ServerResponse setHeader(final HeaderName headerName, final String value) {
|
||||
return setHeader(headerName.getValue(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应头,如果已经存在,则覆盖
|
||||
*
|
||||
* @param header 头key
|
||||
* @param value 值列表,如果为空,删除该header
|
||||
* @return this
|
||||
*/
|
||||
default ServerResponse setHeader(final String header, final List<String> value) {
|
||||
// 去除原有header
|
||||
setHeader(header, (String) null);
|
||||
if(null == value){
|
||||
return this;
|
||||
}
|
||||
|
||||
// 加入新的header
|
||||
for (final String valueItem : value) {
|
||||
addHeader(header, valueItem);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Content-Type头,类似于:text/html;charset=utf-8<br>
|
||||
* 如果用户传入的信息无charset信息,自动根据charset补充,charset设置见{@link #setCharset(Charset)}
|
||||
*
|
||||
* @param contentType Content-Type头内容
|
||||
* @return this
|
||||
*/
|
||||
default ServerResponse setContentType(String contentType) {
|
||||
if (null != contentType) {
|
||||
final Charset charset = getCharset();
|
||||
if (null != charset && !contentType.contains(";charset=")) {
|
||||
contentType = ContentType.build(contentType, charset);
|
||||
}
|
||||
}
|
||||
|
||||
return setHeader(HeaderName.CONTENT_TYPE, contentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Content-Length头,-1表示移除头
|
||||
*
|
||||
* @param contentLength Content-Length头内容
|
||||
* @return this
|
||||
*/
|
||||
default ServerResponse setContentLength(final long contentLength) {
|
||||
return setHeader(HeaderName.CONTENT_LENGTH,
|
||||
contentLength < 0 ? null : String.valueOf(contentLength));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取输出流,用于写出数据
|
||||
*
|
||||
* @return 输出流
|
||||
*/
|
||||
OutputStream getOutputStream();
|
||||
|
||||
/**
|
||||
* 获取响应数据流
|
||||
*
|
||||
* @return 响应数据流
|
||||
*/
|
||||
default PrintWriter getWriter() {
|
||||
final Charset charset = ObjUtil.defaultIfNull(getCharset(), DEFAULT_CHARSET);
|
||||
return new PrintWriter(new OutputStreamWriter(getOutputStream(), charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数据到客户端
|
||||
*
|
||||
* @param data 数据
|
||||
* @param contentType Content-Type类型
|
||||
* @return this
|
||||
*/
|
||||
default ServerResponse write(final String data, final String contentType) {
|
||||
setContentType(contentType);
|
||||
return write(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数据到客户端
|
||||
*
|
||||
* @param data 数据
|
||||
* @return this
|
||||
*/
|
||||
default ServerResponse write(final String data) {
|
||||
final Charset charset = ObjUtil.defaultIfNull(getCharset(), DEFAULT_CHARSET);
|
||||
return write(ByteUtil.toBytes(data, charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数据到客户端
|
||||
*
|
||||
* @param data 数据
|
||||
* @param contentType 返回的类型
|
||||
* @return this
|
||||
*/
|
||||
default ServerResponse write(final byte[] data, final String contentType) {
|
||||
setContentType(contentType);
|
||||
return write(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数据到客户端
|
||||
*
|
||||
* @param data 数据
|
||||
* @return this
|
||||
*/
|
||||
default ServerResponse write(final byte[] data) {
|
||||
final ByteArrayInputStream in = new ByteArrayInputStream(data);
|
||||
return write(in, in.available());
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数据给客户端
|
||||
*
|
||||
* @param in 需要返回客户端的内容
|
||||
* @param contentType 返回的类型
|
||||
* @return this
|
||||
* @since 5.2.6
|
||||
*/
|
||||
default ServerResponse write(final InputStream in, final String contentType) {
|
||||
return write(in, 0, contentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数据给客户端
|
||||
*
|
||||
* @param in 需要返回客户端的内容
|
||||
* @param length 内容长度,默认0表示不定长度,会输出Transfer-encoding: chunked
|
||||
* @param contentType 返回的类型
|
||||
* @return this
|
||||
* @since 5.2.7
|
||||
*/
|
||||
default ServerResponse write(final InputStream in, final int length, final String contentType) {
|
||||
setContentType(contentType);
|
||||
return write(in, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数据到客户端
|
||||
*
|
||||
* @param in 数据流
|
||||
* @return this
|
||||
*/
|
||||
default ServerResponse write(final InputStream in) {
|
||||
return write(in, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数据到客户端
|
||||
*
|
||||
* @param in 数据流
|
||||
* @param length 指定响应内容长度,默认0表示不定长度,会输出Transfer-encoding: chunked
|
||||
* @return this
|
||||
*/
|
||||
default ServerResponse write(final InputStream in, final int length) {
|
||||
setContentLength(length);
|
||||
OutputStream out = null;
|
||||
try {
|
||||
out = getOutputStream();
|
||||
IoUtil.copy(in, out);
|
||||
} finally {
|
||||
IoUtil.closeQuietly(out);
|
||||
IoUtil.closeQuietly(in);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回文件给客户端(文件下载)
|
||||
*
|
||||
* @param file 写出的文件对象
|
||||
* @return this
|
||||
* @since 5.2.6
|
||||
*/
|
||||
default ServerResponse write(final File file) {
|
||||
return write(file, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回文件给客户端(文件下载)
|
||||
*
|
||||
* @param file 写出的文件对象
|
||||
* @param fileName 文件名
|
||||
* @return this
|
||||
*/
|
||||
default ServerResponse write(final File file, String fileName) {
|
||||
final long fileSize = file.length();
|
||||
if (fileSize > Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("File size is too bigger than " + Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(fileName)) {
|
||||
fileName = file.getName();
|
||||
}
|
||||
final String contentType = FileUtil.getMimeType(fileName, ContentType.OCTET_STREAM.getValue());
|
||||
BufferedInputStream in = null;
|
||||
try {
|
||||
in = FileUtil.getInputStream(file);
|
||||
write(in, (int) fileSize, contentType, fileName);
|
||||
} finally {
|
||||
IoUtil.closeQuietly(in);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回文件数据给客户端(文件下载)
|
||||
*
|
||||
* @param in 需要返回客户端的内容
|
||||
* @param contentType 返回的类型
|
||||
* @param fileName 文件名
|
||||
* @return this
|
||||
*/
|
||||
default ServerResponse write(final InputStream in, final String contentType, final String fileName) {
|
||||
return write(in, 0, contentType, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回文件数据给客户端(文件下载)
|
||||
*
|
||||
* @param in 需要返回客户端的内容
|
||||
* @param length 长度
|
||||
* @param contentType 返回的类型
|
||||
* @param fileName 文件名
|
||||
* @return this
|
||||
*/
|
||||
default ServerResponse write(final InputStream in, final int length, final String contentType, final String fileName) {
|
||||
final Charset charset = ObjUtil.defaultIfNull(getCharset(), DEFAULT_CHARSET);
|
||||
|
||||
if (!contentType.startsWith("text/")) {
|
||||
// 非文本类型数据直接走下载
|
||||
setHeader(HeaderName.CONTENT_DISPOSITION, StrUtil.format("attachment;filename={}", UrlEncoder.encodeAll(fileName, charset)));
|
||||
}
|
||||
return write(in, length, contentType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server.engine;
|
||||
|
||||
import org.dromara.hutool.http.server.ServerConfig;
|
||||
import org.dromara.hutool.http.server.handler.HttpHandler;
|
||||
|
||||
/**
|
||||
* 服务端引擎抽象类,实现重置引擎功能
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
public abstract class AbstractServerEngine implements ServerEngine {
|
||||
|
||||
protected ServerConfig config;
|
||||
protected HttpHandler handler;
|
||||
|
||||
@Override
|
||||
public AbstractServerEngine init(final ServerConfig config) {
|
||||
this.config = config;
|
||||
reset();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractServerEngine setHandler(final HttpHandler handler) {
|
||||
this.handler = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置引擎
|
||||
*/
|
||||
protected abstract void reset();
|
||||
|
||||
/**
|
||||
* 初始化引擎,实现逻辑中如果初始化完成,不再重新初始化
|
||||
*/
|
||||
protected abstract void initEngine();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server.engine;
|
||||
|
||||
import org.dromara.hutool.http.server.ServerConfig;
|
||||
import org.dromara.hutool.http.server.handler.HttpHandler;
|
||||
|
||||
/**
|
||||
* HTTP服务器引擎
|
||||
*
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public interface ServerEngine {
|
||||
|
||||
/**
|
||||
* 初始化HTTP服务器
|
||||
*
|
||||
* @param config 配置项
|
||||
* @return this
|
||||
*/
|
||||
ServerEngine init(ServerConfig config);
|
||||
|
||||
/**
|
||||
* 设置请求处理器
|
||||
*
|
||||
* @param handler 请求处理器
|
||||
* @return this
|
||||
*/
|
||||
ServerEngine setHandler(HttpHandler handler);
|
||||
|
||||
/**
|
||||
* 启动HTTP服务器
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* 获取原始引擎的钩子方法,用于自定义特殊属性,如插件等
|
||||
*
|
||||
* @return 对应HTTP服务器实现的引擎对象
|
||||
* @since 6.0.0
|
||||
*/
|
||||
Object getRawEngine();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@link com.sun.net.httpserver.HttpServer} 封装
|
||||
* Jetty引擎实现
|
||||
*
|
||||
* @author looly
|
||||
* @author Looly
|
||||
*/
|
||||
package org.dromara.hutool.http.server.action;
|
||||
package org.dromara.hutool.http.server.engine.jetty;
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* HTTP服务器引擎包
|
||||
*
|
||||
* @author Looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
package org.dromara.hutool.http.server.engine;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server;
|
||||
package org.dromara.hutool.http.server.engine.sun;
|
||||
|
||||
import com.sun.net.httpserver.Headers;
|
||||
import com.sun.net.httpserver.HttpContext;
|
||||
@@ -36,8 +36,8 @@ import java.net.URI;
|
||||
public class HttpExchangeWrapper extends HttpExchange implements Wrapper<HttpExchange> {
|
||||
|
||||
private final HttpExchange raw;
|
||||
private final HttpServerRequest request;
|
||||
private final HttpServerResponse response;
|
||||
private final SunServerRequest request;
|
||||
private final SunServerResponse response;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
@@ -46,8 +46,8 @@ public class HttpExchangeWrapper extends HttpExchange implements Wrapper<HttpExc
|
||||
*/
|
||||
public HttpExchangeWrapper(final HttpExchange raw) {
|
||||
this.raw = raw;
|
||||
this.request = new HttpServerRequest(this);
|
||||
this.response = new HttpServerResponse(this);
|
||||
this.request = new SunServerRequest(this);
|
||||
this.response = new SunServerResponse(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,7 +60,7 @@ public class HttpExchangeWrapper extends HttpExchange implements Wrapper<HttpExc
|
||||
*
|
||||
* @return 请求
|
||||
*/
|
||||
public HttpServerRequest getRequest() {
|
||||
public SunServerRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public class HttpExchangeWrapper extends HttpExchange implements Wrapper<HttpExc
|
||||
*
|
||||
* @return 响应
|
||||
*/
|
||||
public HttpServerResponse getResponse() {
|
||||
public SunServerResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -14,25 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server;
|
||||
package org.dromara.hutool.http.server.engine.sun;
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.core.lang.Console;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.core.thread.GlobalThreadPool;
|
||||
import org.dromara.hutool.http.server.action.Action;
|
||||
import org.dromara.hutool.http.server.action.RootAction;
|
||||
import org.dromara.hutool.http.server.filter.HttpFilter;
|
||||
import org.dromara.hutool.http.server.filter.SimpleFilter;
|
||||
import org.dromara.hutool.http.server.handler.ActionHandler;
|
||||
import org.dromara.hutool.http.server.ServerConfig;
|
||||
import org.dromara.hutool.http.server.engine.sun.filter.HttpFilter;
|
||||
import org.dromara.hutool.http.server.engine.sun.filter.SimpleFilter;
|
||||
import org.dromara.hutool.http.server.handler.RootHandler;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
@@ -43,8 +38,7 @@ import java.util.concurrent.Executor;
|
||||
*/
|
||||
public class SimpleServer {
|
||||
|
||||
private final HttpServer server;
|
||||
private final List<Filter> filters;
|
||||
private final SunHttpServerEngine engine;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
@@ -71,39 +65,23 @@ public class SimpleServer {
|
||||
* @param address 监听地址
|
||||
*/
|
||||
public SimpleServer(final InetSocketAddress address) {
|
||||
this(address, (HttpsConfigurator) null);
|
||||
this(address, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param address 监听地址
|
||||
* @param address 监听地址
|
||||
* @param sslContext ssl配置
|
||||
*/
|
||||
public SimpleServer(final InetSocketAddress address, final SSLContext sslContext) {
|
||||
this(address, new HttpsConfigurator(sslContext));
|
||||
}
|
||||
this.engine = new SunHttpServerEngine();
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param address 监听地址
|
||||
* @param configurator https配置信息,用于使用自定义SSL(TLS)证书等
|
||||
*/
|
||||
public SimpleServer(final InetSocketAddress address, final HttpsConfigurator configurator) {
|
||||
try {
|
||||
if(null != configurator){
|
||||
final HttpsServer server = HttpsServer.create(address, 0);
|
||||
server.setHttpsConfigurator(configurator);
|
||||
this.server = server;
|
||||
} else{
|
||||
this.server = HttpServer.create(address, 0);
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
setExecutor(GlobalThreadPool.getExecutor());
|
||||
filters = new ArrayList<>();
|
||||
final ServerConfig serverConfig = ServerConfig.of()
|
||||
.setHost(address.getHostName())
|
||||
.setPort(address.getPort())
|
||||
.setSslContext(sslContext);
|
||||
this.engine.init(serverConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,7 +93,6 @@ public class SimpleServer {
|
||||
* <li>{@link #setRoot(String)} </li>
|
||||
* <li>{@link #createContext(String, HttpHandler)} </li>
|
||||
* <li>{@link #addHandler(String, HttpHandler)}</li>
|
||||
* <li>{@link #addAction(String, Action)} </li>
|
||||
* </ul>
|
||||
*
|
||||
* @param filter {@link Filter} 请求过滤器
|
||||
@@ -123,7 +100,7 @@ public class SimpleServer {
|
||||
* @since 5.5.7
|
||||
*/
|
||||
public SimpleServer addFilter(final Filter filter) {
|
||||
this.filters.add(filter);
|
||||
this.engine.addFilter(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -136,7 +113,6 @@ public class SimpleServer {
|
||||
* <li>{@link #setRoot(String)} </li>
|
||||
* <li>{@link #createContext(String, HttpHandler)} </li>
|
||||
* <li>{@link #addHandler(String, HttpHandler)}</li>
|
||||
* <li>{@link #addAction(String, Action)} </li>
|
||||
* </ul>
|
||||
*
|
||||
* @param filter {@link Filter} 请求过滤器
|
||||
@@ -177,10 +153,22 @@ public class SimpleServer {
|
||||
public HttpContext createContext(String path, final HttpHandler handler) {
|
||||
// 非/开头的路径会报错
|
||||
path = StrUtil.addPrefixIfNot(path, StrUtil.SLASH);
|
||||
final HttpContext context = this.server.createContext(path, handler);
|
||||
// 增加整体过滤器
|
||||
context.getFilters().addAll(this.filters);
|
||||
return context;
|
||||
return this.engine.createContext(path, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加请求处理规则,使用默认的{@link RootHandler},默认从当前项目根目录读取页面
|
||||
*
|
||||
* @param path 路径,例如:/a/b 或者 a/b
|
||||
* @param action 处理器,包括请求和响应处理
|
||||
* @return this
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public SimpleServer addAction(final String path, final org.dromara.hutool.http.server.handler.HttpHandler action) {
|
||||
return addHandler(path, exchange -> {
|
||||
final HttpExchangeWrapper exchangeWrapper = new HttpExchangeWrapper(exchange);
|
||||
action.handle(exchangeWrapper.getRequest(), exchangeWrapper.getResponse());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,18 +188,8 @@ public class SimpleServer {
|
||||
* @return this
|
||||
*/
|
||||
public SimpleServer setRoot(final File root) {
|
||||
return addAction("/", new RootAction(root));
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加请求处理规则
|
||||
*
|
||||
* @param path 路径
|
||||
* @param action 处理器
|
||||
* @return this
|
||||
*/
|
||||
public SimpleServer addAction(final String path, final Action action) {
|
||||
return addHandler(path, new ActionHandler(action));
|
||||
this.engine.setHandler(new RootHandler(root));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,7 +199,7 @@ public class SimpleServer {
|
||||
* @return this
|
||||
*/
|
||||
public SimpleServer setExecutor(final Executor executor) {
|
||||
this.server.setExecutor(executor);
|
||||
this.engine.setExecutor(executor);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -231,7 +209,7 @@ public class SimpleServer {
|
||||
* @return {@link HttpServer}
|
||||
*/
|
||||
public HttpServer getRawServer() {
|
||||
return this.server;
|
||||
return this.engine.getRawEngine();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,7 +218,7 @@ public class SimpleServer {
|
||||
* @return {@link InetSocketAddress}
|
||||
*/
|
||||
public InetSocketAddress getAddress() {
|
||||
return this.server.getAddress();
|
||||
return getRawServer().getAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,6 +227,6 @@ public class SimpleServer {
|
||||
public void start() {
|
||||
final InetSocketAddress address = getAddress();
|
||||
Console.log("Hutool Simple Http Server listen on 【{}:{}】", address.getHostName(), address.getPort());
|
||||
this.server.start();
|
||||
this.engine.start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server.engine.sun;
|
||||
|
||||
import com.sun.net.httpserver.*;
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.core.thread.GlobalThreadPool;
|
||||
import org.dromara.hutool.http.server.engine.AbstractServerEngine;
|
||||
import org.dromara.hutool.http.server.ServerConfig;
|
||||
import org.dromara.hutool.http.server.engine.sun.filter.HttpFilter;
|
||||
import org.dromara.hutool.http.server.engine.sun.filter.SimpleFilter;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* 基于Sun HttpServer的HTTP服务器引擎实现
|
||||
*
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class SunHttpServerEngine extends AbstractServerEngine {
|
||||
|
||||
private HttpServer server;
|
||||
private List<Filter> filters;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public SunHttpServerEngine() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
initEngine();
|
||||
this.server.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpServer getRawEngine() {
|
||||
return this.server;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加请求过滤器,此过滤器对所有请求有效<br>
|
||||
* 此方法需在以下方法前之前调用:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link #createContext(String, HttpHandler)} </li>
|
||||
* </ul>
|
||||
*
|
||||
* @param filter {@link Filter} 请求过滤器
|
||||
* @return this
|
||||
* @since 5.5.7
|
||||
*/
|
||||
public SunHttpServerEngine addFilter(final Filter filter) {
|
||||
if (null == this.filters) {
|
||||
this.filters = new ArrayList<>();
|
||||
}
|
||||
this.filters.add(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加请求过滤器,此过滤器对所有请求有效<br>
|
||||
* 此方法需在以下方法前之前调用:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link #createContext(String, HttpHandler)} </li>
|
||||
* </ul>
|
||||
*
|
||||
* @param filter {@link Filter} 请求过滤器
|
||||
* @return this
|
||||
* @since 5.5.7
|
||||
*/
|
||||
public SunHttpServerEngine addFilter(final HttpFilter filter) {
|
||||
return addFilter(new SimpleFilter() {
|
||||
@Override
|
||||
public void doFilter(final HttpExchange httpExchange, final Chain chain) throws IOException {
|
||||
final HttpExchangeWrapper httpExchangeWrapper = new HttpExchangeWrapper(httpExchange);
|
||||
filter.doFilter(httpExchangeWrapper.getRequest(), httpExchangeWrapper.getResponse(), chain);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建请求映射上下文,创建后,用户访问指定路径可使用{@link HttpHandler} 中的规则进行处理
|
||||
*
|
||||
* @param path 路径,例如:/a/b 或者 a/b
|
||||
* @param handler 处理器,包括请求和响应处理
|
||||
* @return {@link HttpContext}
|
||||
* @since 5.5.7
|
||||
*/
|
||||
public HttpContext createContext(String path, final HttpHandler handler) {
|
||||
// 非/开头的路径会报错
|
||||
path = StrUtil.addPrefixIfNot(path, StrUtil.SLASH);
|
||||
final HttpContext context = this.server.createContext(path, handler);
|
||||
// 增加整体过滤器
|
||||
context.getFilters().addAll(this.filters);
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置自定义线程池
|
||||
*
|
||||
* @param executor {@link Executor}
|
||||
* @return this
|
||||
*/
|
||||
public SunHttpServerEngine setExecutor(final Executor executor) {
|
||||
this.server.setExecutor(executor);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reset() {
|
||||
if (null != this.server) {
|
||||
this.server.stop(0);
|
||||
this.server = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initEngine() {
|
||||
final ServerConfig config = this.config;
|
||||
final InetSocketAddress address = new InetSocketAddress(config.getHost(), config.getPort());
|
||||
final SSLContext sslContext = config.getSslContext();
|
||||
try {
|
||||
if (null != sslContext) {
|
||||
final HttpsServer server = HttpsServer.create(address, 0);
|
||||
server.setHttpsConfigurator(new HttpsConfigurator(sslContext));
|
||||
this.server = server;
|
||||
} else {
|
||||
this.server = HttpServer.create(address, 0);
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
setExecutor(GlobalThreadPool.getExecutor());
|
||||
createContext("/", exchange -> SunHttpServerEngine.this.handler.handle(
|
||||
new SunServerRequest(exchange),
|
||||
new SunServerResponse(exchange)
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -14,14 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server;
|
||||
package org.dromara.hutool.http.server.engine.sun;
|
||||
|
||||
import org.dromara.hutool.core.util.CharsetUtil;
|
||||
import com.sun.net.httpserver.HttpContext;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* HttpServer公用对象,提供HttpExchange包装和公用方法
|
||||
@@ -29,18 +27,16 @@ import java.nio.charset.Charset;
|
||||
* @author looly
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public class HttpServerBase implements Closeable {
|
||||
public class SunServerBase implements Closeable {
|
||||
|
||||
final static Charset DEFAULT_CHARSET = CharsetUtil.UTF_8;
|
||||
|
||||
final HttpExchange httpExchange;
|
||||
protected final HttpExchange httpExchange;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param httpExchange {@link HttpExchange}
|
||||
*/
|
||||
public HttpServerBase(final HttpExchange httpExchange) {
|
||||
public SunServerBase(final HttpExchange httpExchange) {
|
||||
this.httpExchange = httpExchange;
|
||||
}
|
||||
|
||||
@@ -49,7 +45,7 @@ public class HttpServerBase implements Closeable {
|
||||
*
|
||||
* @return {@link HttpExchange}对象
|
||||
*/
|
||||
public HttpExchange getHttpExchange() {
|
||||
public HttpExchange getExchange() {
|
||||
return this.httpExchange;
|
||||
}
|
||||
|
||||
@@ -60,7 +56,7 @@ public class HttpServerBase implements Closeable {
|
||||
* @since 5.5.7
|
||||
*/
|
||||
public HttpContext getHttpContext() {
|
||||
return getHttpExchange().getHttpContext();
|
||||
return getExchange().getHttpContext();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server;
|
||||
package org.dromara.hutool.http.server.engine.sun;
|
||||
|
||||
import com.sun.net.httpserver.Headers;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
@@ -26,19 +26,15 @@ import org.dromara.hutool.core.map.CaseInsensitiveMap;
|
||||
import org.dromara.hutool.core.map.MapUtil;
|
||||
import org.dromara.hutool.core.map.multi.ListValueMap;
|
||||
import org.dromara.hutool.core.net.NetUtil;
|
||||
import org.dromara.hutool.http.multipart.MultipartFormData;
|
||||
import org.dromara.hutool.http.multipart.UploadSetting;
|
||||
import org.dromara.hutool.core.net.url.UrlQueryUtil;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.core.util.CharsetUtil;
|
||||
import org.dromara.hutool.core.util.ObjUtil;
|
||||
import org.dromara.hutool.http.meta.ContentTypeUtil;
|
||||
import org.dromara.hutool.http.meta.HeaderName;
|
||||
import org.dromara.hutool.http.meta.Method;
|
||||
import org.dromara.hutool.http.useragent.UserAgent;
|
||||
import org.dromara.hutool.http.useragent.UserAgentUtil;
|
||||
import org.dromara.hutool.http.multipart.MultipartFormData;
|
||||
import org.dromara.hutool.http.multipart.UploadSetting;
|
||||
import org.dromara.hutool.http.server.ServerRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpCookie;
|
||||
import java.net.URI;
|
||||
@@ -47,12 +43,11 @@ import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Http请求对象,对{@link HttpExchange}封装
|
||||
* Sun Http请求包装
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.2.6
|
||||
* @author Looly
|
||||
*/
|
||||
public class HttpServerRequest extends HttpServerBase {
|
||||
public class SunServerRequest extends SunServerBase implements ServerRequest {
|
||||
|
||||
private Map<String, HttpCookie> cookieCache;
|
||||
private ListValueMap<String, String> paramsCache;
|
||||
@@ -65,37 +60,15 @@ public class HttpServerRequest extends HttpServerBase {
|
||||
*
|
||||
* @param httpExchange {@link HttpExchange}
|
||||
*/
|
||||
public HttpServerRequest(final HttpExchange httpExchange) {
|
||||
public SunServerRequest(final HttpExchange httpExchange) {
|
||||
super(httpExchange);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得Http Method
|
||||
*
|
||||
* @return Http Method
|
||||
*/
|
||||
@Override
|
||||
public String getMethod() {
|
||||
return this.httpExchange.getRequestMethod();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为GET请求
|
||||
*
|
||||
* @return 是否为GET请求
|
||||
*/
|
||||
public boolean isGetMethod() {
|
||||
return Method.GET.name().equalsIgnoreCase(getMethod());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为POST请求
|
||||
*
|
||||
* @return 是否为POST请求
|
||||
*/
|
||||
public boolean isPostMethod() {
|
||||
return Method.POST.name().equalsIgnoreCase(getMethod());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得请求URI
|
||||
*
|
||||
@@ -105,20 +78,12 @@ public class HttpServerRequest extends HttpServerBase {
|
||||
return this.httpExchange.getRequestURI();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得请求路径Path
|
||||
*
|
||||
* @return 请求路径
|
||||
*/
|
||||
@Override
|
||||
public String getPath() {
|
||||
return getURI().getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求参数
|
||||
*
|
||||
* @return 参数字符串
|
||||
*/
|
||||
@Override
|
||||
public String getQuery() {
|
||||
return getURI().getQuery();
|
||||
}
|
||||
@@ -132,59 +97,12 @@ public class HttpServerRequest extends HttpServerBase {
|
||||
return this.httpExchange.getRequestHeaders();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得请求header中的信息
|
||||
*
|
||||
* @param headerNameKey 头信息的KEY
|
||||
* @return header值
|
||||
*/
|
||||
public String getHeader(final HeaderName headerNameKey) {
|
||||
return getHeader(headerNameKey.toString());
|
||||
@Override
|
||||
public String getHeader(final String name) {
|
||||
return getHeaders().getFirst(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得请求header中的信息
|
||||
*
|
||||
* @param headerKey 头信息的KEY
|
||||
* @return header值
|
||||
*/
|
||||
public String getHeader(final String headerKey) {
|
||||
return getHeaders().getFirst(headerKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得请求header中的信息
|
||||
*
|
||||
* @param headerKey 头信息的KEY
|
||||
* @param charset 字符集
|
||||
* @return header值
|
||||
*/
|
||||
public String getHeader(final String headerKey, final Charset charset) {
|
||||
final String header = getHeader(headerKey);
|
||||
if (null != header) {
|
||||
return CharsetUtil.convert(header, CharsetUtil.ISO_8859_1, charset);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Content-Type头信息
|
||||
*
|
||||
* @return Content-Type头信息
|
||||
*/
|
||||
public String getContentType() {
|
||||
return getHeader(HeaderName.CONTENT_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取编码,获取失败默认使用UTF-8,获取规则如下:
|
||||
*
|
||||
* <pre>
|
||||
* 1、从Content-Type头中获取编码,类似于:text/html;charset=utf-8
|
||||
* </pre>
|
||||
*
|
||||
* @return 编码,默认UTF-8
|
||||
*/
|
||||
@Override
|
||||
public Charset getCharset() {
|
||||
if(null == this.charsetCache){
|
||||
final String contentType = getContentType();
|
||||
@@ -194,33 +112,6 @@ public class HttpServerRequest extends HttpServerBase {
|
||||
return this.charsetCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得User-Agent
|
||||
*
|
||||
* @return User-Agent字符串
|
||||
*/
|
||||
public String getUserAgentStr() {
|
||||
return getHeader(HeaderName.USER_AGENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得User-Agent,未识别返回null
|
||||
*
|
||||
* @return User-Agent字符串,未识别返回null
|
||||
*/
|
||||
public UserAgent getUserAgent() {
|
||||
return UserAgentUtil.parse(getUserAgentStr());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得Cookie信息字符串
|
||||
*
|
||||
* @return cookie字符串
|
||||
*/
|
||||
public String getCookiesStr() {
|
||||
return getHeader(HeaderName.COOKIE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得Cookie信息列表
|
||||
*
|
||||
@@ -238,9 +129,9 @@ public class HttpServerRequest extends HttpServerBase {
|
||||
public Map<String, HttpCookie> getCookieMap() {
|
||||
if (null == this.cookieCache) {
|
||||
cookieCache = MapUtil.view(MapUtil.putAll(
|
||||
new CaseInsensitiveMap<>(),
|
||||
NetUtil.parseCookies(getCookiesStr()),
|
||||
HttpCookie::getName));
|
||||
new CaseInsensitiveMap<>(),
|
||||
NetUtil.parseCookies(getCookiesStr()),
|
||||
HttpCookie::getName));
|
||||
}
|
||||
return cookieCache;
|
||||
}
|
||||
@@ -255,60 +146,7 @@ public class HttpServerRequest extends HttpServerBase {
|
||||
return getCookieMap().get(cookieName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为Multipart类型表单,此类型表单用于文件上传
|
||||
*
|
||||
* @return 是否为Multipart类型表单,此类型表单用于文件上传
|
||||
*/
|
||||
public boolean isMultipart() {
|
||||
if (!isPostMethod()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final String contentType = getContentType();
|
||||
if (StrUtil.isBlank(contentType)) {
|
||||
return false;
|
||||
}
|
||||
return contentType.toLowerCase().startsWith("multipart/");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求体文本,可以是form表单、json、xml等任意内容<br>
|
||||
* 使用{@link #getCharset()}判断编码,判断失败使用UTF-8编码
|
||||
*
|
||||
* @return 请求
|
||||
*/
|
||||
public String getBody() {
|
||||
return getBody(getCharset());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求体文本,可以是form表单、json、xml等任意内容
|
||||
*
|
||||
* @param charset 编码
|
||||
* @return 请求
|
||||
*/
|
||||
public String getBody(final Charset charset) {
|
||||
return StrUtil.str(getBodyBytes(), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取body的bytes数组
|
||||
*
|
||||
* @return body的bytes数组
|
||||
*/
|
||||
public byte[] getBodyBytes(){
|
||||
if(null == this.bodyCache){
|
||||
this.bodyCache = IoUtil.readBytes(getBodyStream(), true);
|
||||
}
|
||||
return this.bodyCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求体的流,流中可以读取请求内容,包括请求表单数据或文件上传数据
|
||||
*
|
||||
* @return 流
|
||||
*/
|
||||
@Override
|
||||
public InputStream getBodyStream() {
|
||||
InputStream bodyStream = this.httpExchange.getRequestBody();
|
||||
|
||||
@@ -330,32 +168,23 @@ public class HttpServerRequest extends HttpServerBase {
|
||||
return bodyStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定名称的参数值,取第一个值
|
||||
* @param name 参数名
|
||||
* @return 参数值
|
||||
* @since 5.5.8
|
||||
*/
|
||||
public String getParam(final String name){
|
||||
return getParams().getValue(name, 0);
|
||||
@Override
|
||||
public byte[] getBodyBytes(){
|
||||
if(null == this.bodyCache){
|
||||
this.bodyCache = IoUtil.readBytes(getBodyStream(), true);
|
||||
}
|
||||
return this.bodyCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定名称的参数值
|
||||
*
|
||||
* @param name 参数名
|
||||
* @return 参数值
|
||||
* @since 5.5.8
|
||||
*/
|
||||
public Collection<String> getParams(final String name){
|
||||
return getParams().get(name);
|
||||
@Override
|
||||
public MultipartFormData getMultipart() throws IORuntimeException {
|
||||
if(null == this.multipartFormDataCache){
|
||||
this.multipartFormDataCache = parseMultipart(new UploadSetting());
|
||||
}
|
||||
return this.multipartFormDataCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数Map
|
||||
*
|
||||
* @return 参数map
|
||||
*/
|
||||
@Override
|
||||
public ListValueMap<String, String> getParams() {
|
||||
if (null == this.paramsCache) {
|
||||
this.paramsCache = new ListValueMap<>();
|
||||
@@ -436,39 +265,4 @@ public class HttpServerRequest extends HttpServerBase {
|
||||
ip = this.httpExchange.getRemoteAddress().getHostName();
|
||||
return NetUtil.getMultistageReverseProxyIp(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得MultiPart表单内容,多用于获得上传的文件
|
||||
*
|
||||
* @return MultipartFormData
|
||||
* @throws IORuntimeException IO异常
|
||||
* @since 5.3.0
|
||||
*/
|
||||
public MultipartFormData getMultipart() throws IORuntimeException {
|
||||
if(null == this.multipartFormDataCache){
|
||||
this.multipartFormDataCache = parseMultipart(new UploadSetting());
|
||||
}
|
||||
return this.multipartFormDataCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得multipart/form-data 表单内容<br>
|
||||
* 包括文件和普通表单数据<br>
|
||||
* 在同一次请求中,此方法只能被执行一次!
|
||||
*
|
||||
* @param uploadSetting 上传文件的设定,包括最大文件大小、保存在内存的边界大小、临时目录、扩展名限定等
|
||||
* @return MultiPart表单
|
||||
* @throws IORuntimeException IO异常
|
||||
* @since 5.3.0
|
||||
*/
|
||||
public MultipartFormData parseMultipart(final UploadSetting uploadSetting) throws IORuntimeException {
|
||||
final MultipartFormData formData = new MultipartFormData(uploadSetting);
|
||||
try {
|
||||
formData.parseRequestStream(getBodyStream(), getCharset());
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
|
||||
return formData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server.engine.sun;
|
||||
|
||||
import com.sun.net.httpserver.Headers;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.core.io.IoUtil;
|
||||
import org.dromara.hutool.http.meta.ContentType;
|
||||
import org.dromara.hutool.http.meta.HttpStatus;
|
||||
import org.dromara.hutool.http.server.ServerResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* SunHttp服务器响应对象
|
||||
*
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class SunServerResponse extends SunServerBase implements ServerResponse {
|
||||
|
||||
private Charset charset;
|
||||
/**
|
||||
* 是否已经发送了Http状态码,如果没有,提前写出状态码
|
||||
*/
|
||||
private boolean isSendCode;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param httpExchange {@link HttpExchange}
|
||||
*/
|
||||
public SunServerResponse(final HttpExchange httpExchange) {
|
||||
super(httpExchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SunServerResponse setStatus(final int statusCode) {
|
||||
return send(statusCode, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送成功状态码
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public SunServerResponse sendOk() {
|
||||
return setStatus(HttpStatus.HTTP_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送成功状态码
|
||||
*
|
||||
* @param bodyLength 响应体长度,默认0表示不定长度,会输出Transfer-encoding: chunked
|
||||
* @return this
|
||||
* @since 5.5.7
|
||||
*/
|
||||
public SunServerResponse sendOk(final int bodyLength) {
|
||||
return send(HttpStatus.HTTP_OK, bodyLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送404错误页
|
||||
*
|
||||
* @param content 错误页页面内容,默认text/html类型
|
||||
* @return this
|
||||
*/
|
||||
public SunServerResponse send404(final String content) {
|
||||
return sendError(HttpStatus.HTTP_NOT_FOUND, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送错误页
|
||||
*
|
||||
* @param errorCode HTTP错误状态码,见HttpStatus
|
||||
* @param content 错误页页面内容,默认text/html类型
|
||||
* @return this
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public SunServerResponse sendError(final int errorCode, final String content) {
|
||||
setStatus(errorCode);
|
||||
setContentType(ContentType.TEXT_HTML.toString());
|
||||
write(content);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送HTTP状态码
|
||||
*
|
||||
* @param httpStatusCode HTTP状态码,见HttpStatus
|
||||
* @param bodyLength 响应体长度,默认0表示不定长度,会输出Transfer-encoding: chunked
|
||||
* @return this
|
||||
*/
|
||||
public SunServerResponse send(final int httpStatusCode, final long bodyLength) {
|
||||
if (this.isSendCode) {
|
||||
throw new IORuntimeException("Http status code has been send!");
|
||||
}
|
||||
|
||||
try {
|
||||
this.httpExchange.sendResponseHeaders(httpStatusCode, bodyLength);
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
|
||||
this.isSendCode = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SunServerResponse setCharset(final Charset charset) {
|
||||
this.charset = charset;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Charset getCharset() {
|
||||
return this.charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得所有响应头,获取后可以添加新的响应头
|
||||
*
|
||||
* @return 响应头
|
||||
*/
|
||||
public Headers getHeaders() {
|
||||
return this.httpExchange.getResponseHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SunServerResponse addHeader(final String header, final String value) {
|
||||
getHeaders().add(header, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SunServerResponse setHeader(final String header, final String value) {
|
||||
getHeaders().set(header, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应头,如果已经存在,则覆盖
|
||||
*
|
||||
* @param header 头key
|
||||
* @param value 值列表
|
||||
* @return this
|
||||
*/
|
||||
public SunServerResponse setHeader(final String header, final List<String> value) {
|
||||
getHeaders().put(header, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置所有响应头,如果已经存在,则覆盖
|
||||
*
|
||||
* @param headers 响应头map
|
||||
* @return this
|
||||
*/
|
||||
public SunServerResponse setHeaders(final Map<String, List<String>> headers) {
|
||||
getHeaders().putAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置属性
|
||||
*
|
||||
* @param name 属性名
|
||||
* @param value 属性值
|
||||
* @return this
|
||||
*/
|
||||
public SunServerResponse setAttr(final String name, final Object value) {
|
||||
this.httpExchange.setAttribute(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
@Override
|
||||
public OutputStream getOutputStream() {
|
||||
if (!this.isSendCode) {
|
||||
sendOk();
|
||||
}
|
||||
return this.httpExchange.getResponseBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出数据到客户端
|
||||
*
|
||||
* @param in 数据流
|
||||
* @param length 指定响应内容长度,默认0表示不定长度,会输出Transfer-encoding: chunked
|
||||
* @return this
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public SunServerResponse write(final InputStream in, final int length) {
|
||||
if (!isSendCode) {
|
||||
sendOk(Math.max(0, length));
|
||||
}
|
||||
OutputStream out = null;
|
||||
try {
|
||||
out = this.httpExchange.getResponseBody();
|
||||
IoUtil.copy(in, out);
|
||||
} finally {
|
||||
IoUtil.closeQuietly(out);
|
||||
IoUtil.closeQuietly(in);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server.filter;
|
||||
package org.dromara.hutool.http.server.engine.sun.filter;
|
||||
|
||||
import org.dromara.hutool.core.exception.ExceptionUtil;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.http.server.HttpServerRequest;
|
||||
import org.dromara.hutool.http.server.HttpServerResponse;
|
||||
import org.dromara.hutool.http.server.engine.sun.SunServerRequest;
|
||||
import org.dromara.hutool.http.server.engine.sun.SunServerResponse;
|
||||
|
||||
/**
|
||||
* 默认异常处理拦截器
|
||||
@@ -31,7 +31,7 @@ public class DefaultExceptionFilter extends ExceptionFilter{
|
||||
private final static String TEMPLATE_ERROR = "<!DOCTYPE html><html><head><title>Hutool - Error report</title><style>h1,h3 {color:white; background-color: gray;}</style></head><body><h1>HTTP Status {} - {}</h1><hr size=\"1\" noshade=\"noshade\" /><p>{}</p><hr size=\"1\" noshade=\"noshade\" /><h3>Hutool</h3></body></html>";
|
||||
|
||||
@Override
|
||||
public void afterException(final HttpServerRequest req, final HttpServerResponse res, final Throwable e) {
|
||||
public void afterException(final SunServerRequest req, final SunServerResponse res, final Throwable e) {
|
||||
String content = ExceptionUtil.stacktraceToString(e);
|
||||
content = content.replace("\n", "<br/>\n");
|
||||
content = StrUtil.format(TEMPLATE_ERROR, 500, req.getURI(), content);
|
||||
@@ -14,11 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server.filter;
|
||||
package org.dromara.hutool.http.server.engine.sun.filter;
|
||||
|
||||
import com.sun.net.httpserver.Filter;
|
||||
import org.dromara.hutool.http.server.HttpServerRequest;
|
||||
import org.dromara.hutool.http.server.HttpServerResponse;
|
||||
import org.dromara.hutool.http.server.engine.sun.SunServerRequest;
|
||||
import org.dromara.hutool.http.server.engine.sun.SunServerResponse;
|
||||
|
||||
/**
|
||||
* 异常处理过滤器
|
||||
@@ -28,9 +28,9 @@ import org.dromara.hutool.http.server.HttpServerResponse;
|
||||
public abstract class ExceptionFilter implements HttpFilter {
|
||||
|
||||
@Override
|
||||
public void doFilter(final HttpServerRequest req, final HttpServerResponse res, final Filter.Chain chain) {
|
||||
public void doFilter(final SunServerRequest req, final SunServerResponse res, final Filter.Chain chain) {
|
||||
try {
|
||||
chain.doFilter(req.getHttpExchange());
|
||||
chain.doFilter(req.getExchange());
|
||||
} catch (final Throwable e) {
|
||||
afterException(req, res, e);
|
||||
}
|
||||
@@ -39,9 +39,9 @@ public abstract class ExceptionFilter implements HttpFilter {
|
||||
/**
|
||||
* 异常之后的处理逻辑
|
||||
*
|
||||
* @param req {@link HttpServerRequest}
|
||||
* @param res {@link HttpServerResponse}
|
||||
* @param req {@link SunServerRequest}
|
||||
* @param res {@link SunServerResponse}
|
||||
* @param e 异常
|
||||
*/
|
||||
public abstract void afterException(final HttpServerRequest req, final HttpServerResponse res, final Throwable e);
|
||||
public abstract void afterException(final SunServerRequest req, final SunServerResponse res, final Throwable e);
|
||||
}
|
||||
@@ -14,11 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server.filter;
|
||||
package org.dromara.hutool.http.server.engine.sun.filter;
|
||||
|
||||
import org.dromara.hutool.http.server.HttpServerRequest;
|
||||
import org.dromara.hutool.http.server.HttpServerResponse;
|
||||
import com.sun.net.httpserver.Filter;
|
||||
import org.dromara.hutool.http.server.engine.sun.SunServerRequest;
|
||||
import org.dromara.hutool.http.server.engine.sun.SunServerResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -33,10 +33,10 @@ public interface HttpFilter {
|
||||
|
||||
/**
|
||||
* 执行过滤
|
||||
* @param req {@link HttpServerRequest} 请求对象,用于获取请求内容
|
||||
* @param res {@link HttpServerResponse} 响应对象,用于写出内容
|
||||
* @param req {@link SunServerRequest} 请求对象,用于获取请求内容
|
||||
* @param res {@link SunServerResponse} 响应对象,用于写出内容
|
||||
* @param chain {@link Filter.Chain}
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
void doFilter(HttpServerRequest req, HttpServerResponse res, Filter.Chain chain) throws IOException;
|
||||
void doFilter(SunServerRequest req, SunServerResponse res, Filter.Chain chain) throws IOException;
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server.filter;
|
||||
package org.dromara.hutool.http.server.engine.sun.filter;
|
||||
|
||||
import com.sun.net.httpserver.Filter;
|
||||
|
||||
@@ -17,4 +17,4 @@
|
||||
/**
|
||||
* {@link com.sun.net.httpserver.Filter} 实现包装
|
||||
*/
|
||||
package org.dromara.hutool.http.server.filter;
|
||||
package org.dromara.hutool.http.server.engine.sun.filter;
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@link com.sun.net.httpserver.HttpServer}引擎实现包
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
package org.dromara.hutool.http.server.engine.sun;
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tomcat引擎实现
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
package org.dromara.hutool.http.server.engine.tomcat;
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server.engine.undertow;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
import org.dromara.hutool.core.lang.Assert;
|
||||
import org.dromara.hutool.http.server.engine.AbstractServerEngine;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
/**
|
||||
* Undertow引擎实现
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
public class UndertowEngine extends AbstractServerEngine {
|
||||
|
||||
private Undertow undertow;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public UndertowEngine() {
|
||||
// issue#IABWBL JDK8下,在IDEA旗舰版加载Spring boot插件时,启动应用不会检查字段类是否存在
|
||||
// 此处构造时调用下这个类,以便触发类是否存在的检查
|
||||
Assert.notNull(Undertow.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
initEngine();
|
||||
undertow.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Undertow getRawEngine() {
|
||||
return this.undertow;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reset() {
|
||||
if(null != this.undertow){
|
||||
this.undertow.stop();
|
||||
this.undertow = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initEngine() {
|
||||
if (null != this.undertow) {
|
||||
return;
|
||||
}
|
||||
final Undertow.Builder builder = Undertow.builder()
|
||||
.setHandler(exchange -> {
|
||||
this.handler.handle(
|
||||
new UndertowRequest(exchange),
|
||||
new UndertowResponse(exchange));
|
||||
});
|
||||
|
||||
final SSLContext sslContext = this.config.getSslContext();
|
||||
if(null != sslContext){
|
||||
builder.addHttpsListener(this.config.getPort(), this.config.getHost(), sslContext);
|
||||
}else{
|
||||
builder.addHttpListener(this.config.getPort(), this.config.getHost());
|
||||
}
|
||||
this.undertow = builder.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server.engine.undertow;
|
||||
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
|
||||
/**
|
||||
* Undertow请求对象基类,用于获取原始Undertow请求对象
|
||||
*
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class UndertowExchangeBase {
|
||||
|
||||
final HttpServerExchange exchange;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param exchange Undertow请求对象
|
||||
*/
|
||||
public UndertowExchangeBase(final HttpServerExchange exchange) {
|
||||
this.exchange = exchange;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始Undertow请求对象
|
||||
*
|
||||
* @return 原始Undertow请求对象
|
||||
*/
|
||||
public HttpServerExchange getExchange(){
|
||||
return this.exchange;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server.engine.undertow;
|
||||
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.util.HeaderMap;
|
||||
import org.dromara.hutool.core.util.CharsetUtil;
|
||||
import org.dromara.hutool.http.server.ServerRequest;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* Undertow请求对象
|
||||
*
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class UndertowRequest extends UndertowExchangeBase implements ServerRequest {
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param exchange Undertow请求对象
|
||||
*/
|
||||
public UndertowRequest(final HttpServerExchange exchange) {
|
||||
super(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethod() {
|
||||
return this.exchange.getRequestMethod().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return this.exchange.getRelativePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQuery() {
|
||||
return this.exchange.getQueryString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeader(final String name) {
|
||||
return this.exchange.getRequestHeaders().getFirst(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有请求头
|
||||
*
|
||||
* @return 请求头
|
||||
*/
|
||||
public HeaderMap getHeaders() {
|
||||
return this.exchange.getRequestHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Charset getCharset() {
|
||||
return CharsetUtil.charset(this.exchange.getRequestCharset());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getBodyStream() {
|
||||
return this.exchange.getInputStream();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server.engine.undertow;
|
||||
|
||||
import io.undertow.io.Sender;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.util.HttpString;
|
||||
import org.dromara.hutool.http.server.ServerResponse;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Undertow响应对象
|
||||
*
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class UndertowResponse extends UndertowExchangeBase implements ServerResponse {
|
||||
|
||||
private Charset charset;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param exchange Undertow exchange
|
||||
*/
|
||||
public UndertowResponse(final HttpServerExchange exchange) {
|
||||
super(exchange);
|
||||
this.charset = DEFAULT_CHARSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始的HttpServerExchange对象
|
||||
*
|
||||
* @return HttpServerExchange对象
|
||||
*/
|
||||
public HttpServerExchange getExchange() {
|
||||
return exchange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerResponse setStatus(final int statusCode) {
|
||||
this.exchange.setStatusCode(statusCode);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerResponse setCharset(final Charset charset) {
|
||||
this.charset = charset;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Charset getCharset() {
|
||||
return this.charset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerResponse addHeader(final String header, final String value) {
|
||||
this.exchange.getResponseHeaders().add(new HttpString(header), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerResponse setHeader(final String header, final String value) {
|
||||
this.exchange.getResponseHeaders().put(new HttpString(header), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerResponse setHeader(final String header, final List<String> value) {
|
||||
this.exchange.getResponseHeaders().putAll(new HttpString(header), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Sender对象,用于发送数据
|
||||
*
|
||||
* @return Sender对象
|
||||
*/
|
||||
public Sender getSender() {
|
||||
return this.exchange.getResponseSender();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() {
|
||||
return this.exchange.getOutputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerResponse write(final byte[] data) {
|
||||
getSender().send(ByteBuffer.wrap(data));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Undertow引擎实现
|
||||
*
|
||||
* @author Looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
package org.dromara.hutool.http.server.engine.undertow;
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server.handler;
|
||||
|
||||
import org.dromara.hutool.http.server.HttpExchangeWrapper;
|
||||
import org.dromara.hutool.http.server.HttpServerRequest;
|
||||
import org.dromara.hutool.http.server.HttpServerResponse;
|
||||
import org.dromara.hutool.http.server.action.Action;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Action处理器,用于将HttpHandler转换为Action形式
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public class ActionHandler implements HttpHandler {
|
||||
|
||||
private final Action action;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param action Action
|
||||
*/
|
||||
public ActionHandler(final Action action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(final HttpExchange httpExchange) throws IOException {
|
||||
final HttpServerRequest request;
|
||||
final HttpServerResponse response;
|
||||
if (httpExchange instanceof HttpExchangeWrapper) {
|
||||
// issue#3343 当使用Filter时,可能读取了请求参数,此时使用共享的req和res,可复用缓存
|
||||
final HttpExchangeWrapper wrapper = (HttpExchangeWrapper) httpExchange;
|
||||
request = wrapper.getRequest();
|
||||
response = wrapper.getResponse();
|
||||
} else {
|
||||
request = new HttpServerRequest(httpExchange);
|
||||
response = new HttpServerResponse(httpExchange);
|
||||
}
|
||||
action.doAction(request, response);
|
||||
httpExchange.close();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,29 +14,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server.action;
|
||||
package org.dromara.hutool.http.server.handler;
|
||||
|
||||
import org.dromara.hutool.http.server.HttpServerRequest;
|
||||
import org.dromara.hutool.http.server.HttpServerResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.dromara.hutool.http.server.ServerRequest;
|
||||
import org.dromara.hutool.http.server.ServerResponse;
|
||||
|
||||
/**
|
||||
* 请求处理接口<br>
|
||||
* 当用户请求某个Path,则调用相应Action的doAction方法
|
||||
* HTTP请求处理器
|
||||
*
|
||||
* @author Looly
|
||||
* @since 5.2.6
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Action {
|
||||
public interface HttpHandler {
|
||||
|
||||
/**
|
||||
* 处理请求
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @param response 响应对象
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException;
|
||||
void handle(ServerRequest request, ServerResponse response);
|
||||
}
|
||||
@@ -14,12 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server.action;
|
||||
package org.dromara.hutool.http.server.handler;
|
||||
|
||||
import org.dromara.hutool.core.collection.ListUtil;
|
||||
import org.dromara.hutool.core.io.file.FileUtil;
|
||||
import org.dromara.hutool.http.server.HttpServerRequest;
|
||||
import org.dromara.hutool.http.server.HttpServerResponse;
|
||||
import org.dromara.hutool.http.meta.HttpStatus;
|
||||
import org.dromara.hutool.http.server.ServerRequest;
|
||||
import org.dromara.hutool.http.server.ServerResponse;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
@@ -30,8 +31,11 @@ import java.util.List;
|
||||
* @author looly
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public class RootAction implements Action {
|
||||
public class RootHandler implements HttpHandler {
|
||||
|
||||
/**
|
||||
* 默认主页文件名
|
||||
*/
|
||||
public static final String DEFAULT_INDEX_FILE_NAME = "index.html";
|
||||
|
||||
private final File rootDir;
|
||||
@@ -42,7 +46,7 @@ public class RootAction implements Action {
|
||||
*
|
||||
* @param rootDir 网页根目录
|
||||
*/
|
||||
public RootAction(final String rootDir) {
|
||||
public RootHandler(final String rootDir) {
|
||||
this(new File(rootDir));
|
||||
}
|
||||
|
||||
@@ -51,7 +55,7 @@ public class RootAction implements Action {
|
||||
*
|
||||
* @param rootDir 网页根目录
|
||||
*/
|
||||
public RootAction(final File rootDir) {
|
||||
public RootHandler(final File rootDir) {
|
||||
this(rootDir, DEFAULT_INDEX_FILE_NAME);
|
||||
}
|
||||
|
||||
@@ -61,7 +65,7 @@ public class RootAction implements Action {
|
||||
* @param rootDir 网页根目录
|
||||
* @param indexFileNames 主页文件名列表
|
||||
*/
|
||||
public RootAction(final String rootDir, final String... indexFileNames) {
|
||||
public RootHandler(final String rootDir, final String... indexFileNames) {
|
||||
this(new File(rootDir), indexFileNames);
|
||||
}
|
||||
|
||||
@@ -72,13 +76,13 @@ public class RootAction implements Action {
|
||||
* @param indexFileNames 主页文件名列表
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public RootAction(final File rootDir, final String... indexFileNames) {
|
||||
public RootHandler(final File rootDir, final String... indexFileNames) {
|
||||
this.rootDir = rootDir;
|
||||
this.indexFileNames = ListUtil.of(indexFileNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAction(final HttpServerRequest request, final HttpServerResponse response) {
|
||||
public void handle(final ServerRequest request, final ServerResponse response) {
|
||||
final String path = request.getPath();
|
||||
|
||||
File file = FileUtil.file(rootDir, path);
|
||||
@@ -99,6 +103,7 @@ public class RootAction implements Action {
|
||||
}
|
||||
}
|
||||
|
||||
response.send404("404 Not Found !");
|
||||
response.setStatus(HttpStatus.HTTP_NOT_FOUND);
|
||||
response.write("404 Not Found !");
|
||||
}
|
||||
}
|
||||
@@ -16,17 +16,16 @@
|
||||
|
||||
package org.dromara.hutool.http.server;
|
||||
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.http.HttpUtil;
|
||||
import org.dromara.hutool.http.server.filter.DefaultExceptionFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.dromara.hutool.http.server.engine.sun.filter.DefaultExceptionFilter;
|
||||
|
||||
public class ExceptionServerTest {
|
||||
public static void main(final String[] args) {
|
||||
HttpUtil.createServer(8888)
|
||||
.addFilter(new DefaultExceptionFilter())
|
||||
.addAction("/", (req, res) -> {
|
||||
throw new IOException("Test Exception");
|
||||
throw new IORuntimeException("Test Exception");
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.dromara.hutool.core.date.DateUtil;
|
||||
import org.dromara.hutool.core.lang.Console;
|
||||
import org.dromara.hutool.core.map.multi.ListValueMap;
|
||||
import org.dromara.hutool.http.HttpUtil;
|
||||
import org.dromara.hutool.http.server.engine.sun.SimpleServer;
|
||||
|
||||
/**
|
||||
* http://localhost:8888/?name=hutool
|
||||
@@ -33,7 +34,7 @@ public class Issue3343Test {
|
||||
Console.log(" > from : " + req.getClientIP());
|
||||
// 过滤器中获取请求参数
|
||||
Console.log(" > params : " + req.getParams());
|
||||
chain.doFilter(req.getHttpExchange());
|
||||
chain.doFilter(req.getExchange());
|
||||
});
|
||||
|
||||
server.addAction("/", Issue3343Test::index);
|
||||
@@ -41,9 +42,9 @@ public class Issue3343Test {
|
||||
server.start();
|
||||
}
|
||||
|
||||
private static void index(HttpServerRequest request, HttpServerResponse response) {
|
||||
private static void index(final ServerRequest request, final ServerResponse response) {
|
||||
// 具体逻辑中再次获取请求参数
|
||||
ListValueMap<String, String> params = request.getParams();
|
||||
final ListValueMap<String, String> params = request.getParams();
|
||||
Console.log("index params: " + params);
|
||||
response.getWriter().write("GOT: " + params);
|
||||
}
|
||||
|
||||
@@ -19,18 +19,19 @@ package org.dromara.hutool.http.server;
|
||||
import org.dromara.hutool.core.data.id.IdUtil;
|
||||
import org.dromara.hutool.http.HttpUtil;
|
||||
import org.dromara.hutool.http.meta.ContentType;
|
||||
import org.dromara.hutool.http.server.engine.sun.SimpleServer;
|
||||
|
||||
public class Issue3723Test {
|
||||
public static void main(final String[] args) {
|
||||
final SimpleServer server = HttpUtil.createServer(8888);
|
||||
server.addFilter((req, res, chain) -> {
|
||||
final String requestId = IdUtil.fastSimpleUUID();
|
||||
req.getHttpExchange().setAttribute("requestId", requestId);
|
||||
req.getExchange().setAttribute("requestId", requestId);
|
||||
res.addHeader("X-Request-Id", requestId);
|
||||
|
||||
res.write("new Content");
|
||||
|
||||
chain.doFilter(req.getHttpExchange());
|
||||
chain.doFilter(req.getExchange());
|
||||
});
|
||||
server.addAction("/", (req, res)-> res.write("Hello Hutool Server", ContentType.JSON.getValue()));
|
||||
server.start();
|
||||
|
||||
@@ -19,8 +19,10 @@ package org.dromara.hutool.http.server;
|
||||
import org.dromara.hutool.core.date.DateUtil;
|
||||
import org.dromara.hutool.core.date.StopWatch;
|
||||
import org.dromara.hutool.core.lang.Console;
|
||||
import org.dromara.hutool.http.multipart.MultipartFormData;
|
||||
import org.dromara.hutool.http.HttpUtil;
|
||||
import org.dromara.hutool.http.multipart.MultipartFormData;
|
||||
import org.dromara.hutool.http.server.engine.sun.SimpleServer;
|
||||
import org.dromara.hutool.http.server.engine.sun.SunServerRequest;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -35,7 +37,7 @@ public class IssueI6Q30XTest {
|
||||
public static void main(String[] args) {
|
||||
final SimpleServer server = HttpUtil.createServer(8888);
|
||||
server.addAction("/file", (request, response) -> {
|
||||
Console.log(request.getHeaders().entrySet());
|
||||
Console.log(((SunServerRequest)request).getHeaders().entrySet());
|
||||
|
||||
final StopWatch stopWatch = DateUtil.createStopWatch();
|
||||
stopWatch.start();
|
||||
|
||||
@@ -19,25 +19,26 @@ package org.dromara.hutool.http.server;
|
||||
import org.dromara.hutool.core.lang.Console;
|
||||
import org.dromara.hutool.http.HttpUtil;
|
||||
import org.dromara.hutool.http.meta.HeaderName;
|
||||
import org.dromara.hutool.http.meta.HttpStatus;
|
||||
|
||||
public class RedirectServerTest {
|
||||
public static void main(final String[] args) {
|
||||
HttpUtil.createServer(8888).addAction("/redirect1", (request, response) -> {
|
||||
response.addHeader(HeaderName.LOCATION.getValue(),"http://localhost:8888/redirect2");
|
||||
response.addHeader(HeaderName.SET_COOKIE.getValue(),"redirect1=1; path=/; HttpOnly");
|
||||
response.send(301);
|
||||
response.setStatus(301);
|
||||
}).addAction("/redirect2", (request, response) -> {
|
||||
response.addHeader(HeaderName.LOCATION.getValue(),"http://localhost:8888/redirect3");
|
||||
response.addHeader(HeaderName.SET_COOKIE.getValue(), "redirect2=2; path=/; HttpOnly");
|
||||
response.send(301);
|
||||
response.setStatus(301);
|
||||
}).addAction("/redirect3", (request, response) -> {
|
||||
response.addHeader(HeaderName.LOCATION.getValue(),"http://localhost:8888/redirect4");
|
||||
response.addHeader(HeaderName.SET_COOKIE.getValue(),"redirect3=3; path=/; HttpOnly");
|
||||
response.send(301);
|
||||
response.setStatus(301);
|
||||
}).addAction("/redirect4", (request, response) -> {
|
||||
final String cookie = request.getHeader(HeaderName.COOKIE);
|
||||
Console.log(cookie);
|
||||
response.sendOk();
|
||||
response.setStatus(HttpStatus.HTTP_OK);
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,11 @@ package org.dromara.hutool.http.server;
|
||||
import org.dromara.hutool.core.collection.ListUtil;
|
||||
import org.dromara.hutool.core.io.file.FileUtil;
|
||||
import org.dromara.hutool.core.lang.Console;
|
||||
import org.dromara.hutool.http.multipart.UploadFile;
|
||||
import org.dromara.hutool.http.HttpUtil;
|
||||
import org.dromara.hutool.http.meta.ContentType;
|
||||
import org.dromara.hutool.http.meta.HeaderName;
|
||||
import org.dromara.hutool.http.HttpUtil;
|
||||
import org.dromara.hutool.http.multipart.UploadFile;
|
||||
import org.dromara.hutool.http.server.engine.sun.SunServerRequest;
|
||||
import org.dromara.hutool.json.JSONUtil;
|
||||
|
||||
import java.net.HttpCookie;
|
||||
@@ -33,13 +34,13 @@ public class SimpleServerTest {
|
||||
HttpUtil.createServer(8888)
|
||||
.addFilter(((req, res, chain) -> {
|
||||
Console.log("Filter: " + req.getPath());
|
||||
chain.doFilter(req.getHttpExchange());
|
||||
chain.doFilter(req.getExchange());
|
||||
}))
|
||||
// 设置默认根目录,classpath/html
|
||||
.setRoot(FileUtil.file("html"))
|
||||
// get数据测试,返回请求的PATH
|
||||
.addAction("/get", (request, response) ->
|
||||
response.write(request.getURI().toString(), ContentType.TEXT_PLAIN.toString())
|
||||
response.write(((SunServerRequest)request).getURI().toString(), ContentType.TEXT_PLAIN.toString())
|
||||
)
|
||||
// 返回JSON数据测试
|
||||
.addAction("/restTest", (request, response) -> {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http.server.engine;
|
||||
|
||||
import org.dromara.hutool.core.lang.Console;
|
||||
import org.dromara.hutool.http.server.ServerConfig;
|
||||
import org.dromara.hutool.http.server.engine.undertow.UndertowEngine;
|
||||
|
||||
public class UndertowTest {
|
||||
public static void main(String[] args) {
|
||||
final UndertowEngine undertowEngine = new UndertowEngine();
|
||||
undertowEngine.init(ServerConfig.of());
|
||||
undertowEngine.setHandler((request, response) -> {
|
||||
Console.log(request.getPath());
|
||||
response.write("Hutool Undertow response test");
|
||||
});
|
||||
undertowEngine.start();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user