mirror of
https://github.com/alibaba/arthas.git
synced 2024-04-21 10:21:39 +00:00
add http api handler
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
package com.taobao.arthas.core.shell.session;
|
||||
|
||||
/**
|
||||
* Arthas Session Manager
|
||||
* @author gongdewei 2020-03-20
|
||||
*/
|
||||
public interface SessionManager {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.taobao.arthas.core.shell.session.impl;
|
||||
|
||||
/**
|
||||
* Arthas Session Manager
|
||||
* @author gongdewei 2020-03-20
|
||||
*/
|
||||
public interface SessionManagerImpl {
|
||||
|
||||
}
|
||||
+32
-14
@@ -7,6 +7,7 @@ import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
||||
import com.taobao.arthas.common.IOUtils;
|
||||
import com.taobao.arthas.core.shell.term.impl.http.api.HttpApiHandler;
|
||||
import com.taobao.arthas.core.util.LogUtil;
|
||||
import com.taobao.middleware.logger.Logger;
|
||||
|
||||
@@ -30,6 +31,7 @@ import io.termd.core.util.Logging;
|
||||
/**
|
||||
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
|
||||
* @author hengyunabc 2019-11-06
|
||||
* @author gongdewei 2020-03-18
|
||||
*/
|
||||
public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
|
||||
private static final Logger logger = LogUtil.getArthasLogger();
|
||||
@@ -38,10 +40,13 @@ public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequ
|
||||
|
||||
private File dir;
|
||||
|
||||
private HttpApiHandler httpApiHandler;
|
||||
|
||||
public HttpRequestHandler(String wsUri, File dir) {
|
||||
this.wsUri = wsUri;
|
||||
this.dir = dir;
|
||||
dir.mkdirs();
|
||||
this.httpApiHandler = new HttpApiHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,30 +58,39 @@ public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequ
|
||||
send100Continue(ctx);
|
||||
}
|
||||
|
||||
HttpResponse response = new DefaultHttpResponse(request.protocolVersion(),
|
||||
HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
|
||||
HttpResponse response = null;
|
||||
String path = new URI(request.uri()).getPath();
|
||||
|
||||
if ("/".equals(path)) {
|
||||
path = "/index.html";
|
||||
}
|
||||
|
||||
try {
|
||||
DefaultFullHttpResponse fileViewResult = DirectoryBrowser.view(dir, path, request.protocolVersion());
|
||||
if (fileViewResult != null) {
|
||||
response = fileViewResult;
|
||||
} else {
|
||||
FullHttpResponse fullResp = readFileFromResource(request, path);
|
||||
if(fullResp != null){
|
||||
response = fullResp;
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.NOT_FOUND);
|
||||
}
|
||||
//handle http restful api
|
||||
if ("/api".equals(path)) {
|
||||
response = httpApiHandler.handle(request);
|
||||
}
|
||||
|
||||
//try classpath resource first
|
||||
if (response == null){
|
||||
response = readFileFromResource(request, path);
|
||||
}
|
||||
|
||||
//try output dir later, avoid overlay classpath resources files
|
||||
if (response == null){
|
||||
response = DirectoryBrowser.view(dir, path, request.protocolVersion());
|
||||
}
|
||||
|
||||
//not found
|
||||
if (response == null){
|
||||
response = createResponse(request, HttpResponseStatus.NOT_FOUND);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.error("arthas", "arthas process http request error: " + request.uri(), e);
|
||||
} finally {
|
||||
//If it is null, an error may occur
|
||||
if (response == null){
|
||||
response = createResponse(request, HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
ctx.write(response);
|
||||
ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
|
||||
future.addListener(ChannelFutureListener.CLOSE);
|
||||
@@ -84,6 +98,10 @@ public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequ
|
||||
}
|
||||
}
|
||||
|
||||
private DefaultHttpResponse createResponse(FullHttpRequest request, HttpResponseStatus status) {
|
||||
return new DefaultHttpResponse(request.protocolVersion(), status);
|
||||
}
|
||||
|
||||
private FullHttpResponse readFileFromResource(FullHttpRequest request, String path) throws IOException {
|
||||
DefaultFullHttpResponse fullResp = null;
|
||||
InputStream in = null;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.taobao.arthas.core.shell.term.impl.http.api;
|
||||
|
||||
/**
|
||||
* Http Api exception
|
||||
* @author gongdewei 2020-03-19
|
||||
*/
|
||||
public class ApiException extends Exception {
|
||||
|
||||
public ApiException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ApiException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.taobao.arthas.core.shell.term.impl.http.api;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Http Api exception
|
||||
* @author gongdewei 2020-03-19
|
||||
*/
|
||||
public class ApiRequest {
|
||||
private String action;
|
||||
private boolean sync;
|
||||
private String command;
|
||||
private Map<String, Object> options;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ApiRequest{" +
|
||||
"action='" + action + '\'' +
|
||||
", sync=" + sync +
|
||||
", command='" + command + '\'' +
|
||||
", options=" + options +
|
||||
'}';
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public boolean isSync() {
|
||||
return sync;
|
||||
}
|
||||
|
||||
public void setSync(boolean sync) {
|
||||
this.sync = sync;
|
||||
}
|
||||
|
||||
public String getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
public void setCommand(String command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public Map<String, Object> getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public void setOptions(Map<String, Object> options) {
|
||||
this.options = options;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.taobao.arthas.core.shell.term.impl.http.api;
|
||||
|
||||
/**
|
||||
* Http Api exception
|
||||
* @author gongdewei 2020-03-19
|
||||
*/
|
||||
public class ApiResponse<T> {
|
||||
private String requestId;
|
||||
private ApiState state;
|
||||
private String message;
|
||||
private String sessionId;
|
||||
private String consumerId;
|
||||
private String jobId;
|
||||
private T body;
|
||||
|
||||
public String getRequestId() {
|
||||
return requestId;
|
||||
}
|
||||
|
||||
public void setRequestId(String requestId) {
|
||||
this.requestId = requestId;
|
||||
}
|
||||
|
||||
public ApiState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(ApiState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
public String getConsumerId() {
|
||||
return consumerId;
|
||||
}
|
||||
|
||||
public void setConsumerId(String consumerId) {
|
||||
this.consumerId = consumerId;
|
||||
}
|
||||
|
||||
public String getJobId() {
|
||||
return jobId;
|
||||
}
|
||||
|
||||
public void setJobId(String jobId) {
|
||||
this.jobId = jobId;
|
||||
}
|
||||
|
||||
public T getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public void setBody(T body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.taobao.arthas.core.shell.term.impl.http.api;
|
||||
|
||||
/**
|
||||
* Http API response state
|
||||
* @author gongdewei 2020-03-19
|
||||
*/
|
||||
public enum ApiState {
|
||||
/** accepted */
|
||||
SCHEDULED,
|
||||
RUNNING,
|
||||
SUCCEEDED,
|
||||
FAILED,
|
||||
REFUSED
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package com.taobao.arthas.core.shell.term.impl.http.api;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.taobao.arthas.core.server.ArthasBootstrap;
|
||||
import com.taobao.arthas.core.shell.ShellServer;
|
||||
import com.taobao.arthas.core.util.LogUtil;
|
||||
import com.taobao.arthas.core.util.StringUtils;
|
||||
import com.taobao.middleware.logger.Logger;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
|
||||
/**
|
||||
* Http Restful Api Handler
|
||||
* @author gongdewei 2020-03-18
|
||||
*/
|
||||
public class HttpApiHandler {
|
||||
|
||||
private static final Logger logger = LogUtil.getArthasLogger();
|
||||
|
||||
public HttpResponse handle(FullHttpRequest request) throws Exception {
|
||||
DefaultFullHttpResponse response = new DefaultFullHttpResponse(request.protocolVersion(),
|
||||
HttpResponseStatus.OK);
|
||||
|
||||
ApiResponse result = null;
|
||||
String requestBody = null;
|
||||
try {
|
||||
HttpMethod method = request.method();
|
||||
if (HttpMethod.POST.equals(method)){
|
||||
requestBody = getBody(request);
|
||||
ApiRequest apiRequest = parseRequest(requestBody);
|
||||
result = processRequest(apiRequest);
|
||||
} else {
|
||||
result = createResponse(ApiState.REFUSED, "Unsupported http method: "+method.name());
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
result = createResponse(ApiState.FAILED, "Process request error: "+e.getMessage());
|
||||
logger.error("arthas", "arthas process http api request error: " + request.uri()+", request body: "+requestBody, e);
|
||||
}
|
||||
if (result == null) {
|
||||
result = createResponse(ApiState.FAILED, "The request was not processed");
|
||||
}
|
||||
|
||||
String jsonResult = JSON.toJSONString(result);
|
||||
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=utf-8");
|
||||
response.content().writeBytes(jsonResult.getBytes("UTF-8"));
|
||||
return response;
|
||||
}
|
||||
|
||||
private ApiRequest parseRequest(String requestBody) throws ApiException {
|
||||
if (StringUtils.isBlank(requestBody)){
|
||||
throw new ApiException("parse request failed: request body is empty");
|
||||
}
|
||||
try {
|
||||
//Object jsonRequest = JSON.parse(requestBody);
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
return objectMapper.readValue(requestBody, ApiRequest.class);
|
||||
} catch (Exception e) {
|
||||
throw new ApiException("parse request failed: "+e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private ApiResponse processRequest(ApiRequest apiRequest) {
|
||||
|
||||
String action = apiRequest.getAction();
|
||||
if ("exec".equalsIgnoreCase(action)){
|
||||
return processExecRequest(apiRequest);
|
||||
} else if("init_session".equalsIgnoreCase(action)){
|
||||
return processInitSessionRequest(apiRequest);
|
||||
} else if("close_session".equalsIgnoreCase(action)){
|
||||
return processCloseSessionRequest(apiRequest);
|
||||
}
|
||||
|
||||
return createResponse(ApiState.REFUSED, "Unsupported action: "+action);
|
||||
}
|
||||
|
||||
private ApiResponse processInitSessionRequest(ApiRequest apiRequest) {
|
||||
// ShellServer shellServer = getShellServer();
|
||||
// shellServer.createShell()
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ApiResponse processCloseSessionRequest(ApiRequest apiRequest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private ApiResponse processExecRequest(ApiRequest apiRequest) {
|
||||
String command = apiRequest.getCommand();
|
||||
//TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
private ApiResponse createResponse(ApiState apiState, String message) {
|
||||
ApiResponse apiResponse = new ApiResponse();
|
||||
apiResponse.setState(apiState);
|
||||
apiResponse.setMessage(message);
|
||||
return apiResponse;
|
||||
}
|
||||
|
||||
private String getBody(FullHttpRequest request){
|
||||
ByteBuf buf = request.content();
|
||||
return buf.toString(CharsetUtil.UTF_8);
|
||||
}
|
||||
|
||||
// private ShellServer getShellServer() {
|
||||
// return ArthasBootstrap.getInstance().getShellServer();
|
||||
// }
|
||||
}
|
||||
Reference in New Issue
Block a user