|
|
|
@ -1,12 +1,12 @@ |
|
|
|
/** |
|
|
|
* Copyright © 2016-2018 The Thingsboard Authors |
|
|
|
* |
|
|
|
* <p> |
|
|
|
* 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
|
|
|
|
* |
|
|
|
* <p> |
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
* <p> |
|
|
|
* 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. |
|
|
|
@ -17,64 +17,68 @@ package org.thingsboard.server.transport.http; |
|
|
|
|
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
import org.springframework.beans.factory.annotation.Value; |
|
|
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|
|
|
import org.springframework.http.HttpStatus; |
|
|
|
import org.springframework.http.ResponseEntity; |
|
|
|
import org.springframework.web.bind.annotation.*; |
|
|
|
import org.springframework.util.StringUtils; |
|
|
|
import org.springframework.web.bind.annotation.PathVariable; |
|
|
|
import org.springframework.web.bind.annotation.RequestBody; |
|
|
|
import org.springframework.web.bind.annotation.RequestMapping; |
|
|
|
import org.springframework.web.bind.annotation.RequestMethod; |
|
|
|
import org.springframework.web.bind.annotation.RequestParam; |
|
|
|
import org.springframework.web.bind.annotation.RestController; |
|
|
|
import org.springframework.web.context.request.async.DeferredResult; |
|
|
|
import org.thingsboard.server.common.transport.SessionMsgProcessor; |
|
|
|
import org.thingsboard.server.common.transport.auth.DeviceAuthService; |
|
|
|
import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService; |
|
|
|
import org.thingsboard.server.common.transport.SessionMsgListener; |
|
|
|
import org.thingsboard.server.common.transport.TransportContext; |
|
|
|
import org.thingsboard.server.common.transport.TransportService; |
|
|
|
import org.thingsboard.server.common.transport.TransportServiceCallback; |
|
|
|
import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
|
|
|
import org.thingsboard.server.gen.transport.TransportProtos; |
|
|
|
import org.thingsboard.server.gen.transport.TransportProtos.*; |
|
|
|
import org.thingsboard.server.transport.http.session.HttpSessionCtx; |
|
|
|
|
|
|
|
import javax.servlet.http.HttpServletRequest; |
|
|
|
import java.util.Arrays; |
|
|
|
import java.util.List; |
|
|
|
import java.util.UUID; |
|
|
|
import java.util.function.Consumer; |
|
|
|
|
|
|
|
/** |
|
|
|
* @author Andrew Shvayka |
|
|
|
*/ |
|
|
|
@RestController |
|
|
|
@ConditionalOnProperty(prefix = "transport.http", value = "enabled", havingValue = "true") |
|
|
|
@ConditionalOnProperty(prefix = "transport.http", value = "enabled", havingValue = "true", matchIfMissing = true) |
|
|
|
@RequestMapping("/api/v1") |
|
|
|
@Slf4j |
|
|
|
public class DeviceApiController { |
|
|
|
|
|
|
|
@Value("${http.request_timeout}") |
|
|
|
private long defaultTimeout; |
|
|
|
|
|
|
|
@Autowired(required = false) |
|
|
|
private SessionMsgProcessor processor; |
|
|
|
|
|
|
|
@Autowired(required = false) |
|
|
|
private DeviceAuthService authService; |
|
|
|
|
|
|
|
@Autowired(required = false) |
|
|
|
private HostRequestsQuotaService quotaService; |
|
|
|
@Autowired |
|
|
|
private HttpTransportContext transportContext; |
|
|
|
|
|
|
|
@RequestMapping(value = "/{deviceToken}/attributes", method = RequestMethod.GET, produces = "application/json") |
|
|
|
public DeferredResult<ResponseEntity> getDeviceAttributes(@PathVariable("deviceToken") String deviceToken, |
|
|
|
@RequestParam(value = "clientKeys", required = false, defaultValue = "") String clientKeys, |
|
|
|
@RequestParam(value = "sharedKeys", required = false, defaultValue = "") String sharedKeys, |
|
|
|
HttpServletRequest httpRequest) { |
|
|
|
DeferredResult<ResponseEntity> responseWriter = new DeferredResult<ResponseEntity>(); |
|
|
|
DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>(); |
|
|
|
if (quotaExceeded(httpRequest, responseWriter)) { |
|
|
|
return responseWriter; |
|
|
|
} |
|
|
|
// HttpSessionCtx ctx = getHttpSessionCtx(responseWriter);
|
|
|
|
// if (ctx.login(new DeviceTokenCredentials(deviceToken))) {
|
|
|
|
// GetAttributesRequest request;
|
|
|
|
// if (StringUtils.isEmpty(clientKeys) && StringUtils.isEmpty(sharedKeys)) {
|
|
|
|
// request = new BasicGetAttributesRequest(0);
|
|
|
|
// } else {
|
|
|
|
// Set<String> clientKeySet = !StringUtils.isEmpty(clientKeys) ? new HashSet<>(Arrays.asList(clientKeys.split(","))) : null;
|
|
|
|
// Set<String> sharedKeySet = !StringUtils.isEmpty(sharedKeys) ? new HashSet<>(Arrays.asList(sharedKeys.split(","))) : null;
|
|
|
|
// request = new BasicGetAttributesRequest(0, clientKeySet, sharedKeySet);
|
|
|
|
// }
|
|
|
|
// process(ctx, request);
|
|
|
|
// } else {
|
|
|
|
// responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
|
|
|
|
// }
|
|
|
|
|
|
|
|
transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), |
|
|
|
new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { |
|
|
|
GetAttributeRequestMsg.Builder request = GetAttributeRequestMsg.newBuilder().setRequestId(0); |
|
|
|
List<String> clientKeySet = !StringUtils.isEmpty(clientKeys) ? Arrays.asList(clientKeys.split(",")) : null; |
|
|
|
List<String> sharedKeySet = !StringUtils.isEmpty(sharedKeys) ? Arrays.asList(sharedKeys.split(",")) : null; |
|
|
|
if (clientKeySet != null) { |
|
|
|
request.addAllClientAttributeNames(clientKeySet); |
|
|
|
} |
|
|
|
if (sharedKeySet != null) { |
|
|
|
request.addAllSharedAttributeNames(sharedKeySet); |
|
|
|
} |
|
|
|
TransportService transportService = transportContext.getTransportService(); |
|
|
|
transportService.registerSession(sessionInfo, TransportProtos.SessionType.SYNC, new HttpSessionListener(transportContext, responseWriter)); |
|
|
|
transportService.process(sessionInfo, request.build(), new SessionCloseOnErrorCallback(transportService, sessionInfo)); |
|
|
|
})); |
|
|
|
return responseWriter; |
|
|
|
} |
|
|
|
|
|
|
|
@ -199,7 +203,7 @@ public class DeviceApiController { |
|
|
|
// }
|
|
|
|
|
|
|
|
private HttpSessionCtx getHttpSessionCtx(DeferredResult<ResponseEntity> responseWriter) { |
|
|
|
return getHttpSessionCtx(responseWriter, defaultTimeout); |
|
|
|
return getHttpSessionCtx(responseWriter, transportContext.getDefaultTimeout()); |
|
|
|
} |
|
|
|
|
|
|
|
private HttpSessionCtx getHttpSessionCtx(DeferredResult<ResponseEntity> responseWriter, long timeout) { |
|
|
|
@ -213,7 +217,7 @@ public class DeviceApiController { |
|
|
|
// }
|
|
|
|
|
|
|
|
private boolean quotaExceeded(HttpServletRequest request, DeferredResult<ResponseEntity> responseWriter) { |
|
|
|
if (quotaService.isQuotaExceeded(request.getRemoteAddr())) { |
|
|
|
if (transportContext.getQuotaService().isQuotaExceeded(request.getRemoteAddr())) { |
|
|
|
log.warn("REST Quota exceeded for [{}] . Disconnect", request.getRemoteAddr()); |
|
|
|
responseWriter.setResult(new ResponseEntity<>(HttpStatus.BANDWIDTH_LIMIT_EXCEEDED)); |
|
|
|
return true; |
|
|
|
@ -221,4 +225,96 @@ public class DeviceApiController { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
private static class DeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> { |
|
|
|
private final TransportContext transportContext; |
|
|
|
private final DeferredResult<ResponseEntity> responseWriter; |
|
|
|
private final Consumer<SessionInfoProto> onSuccess; |
|
|
|
|
|
|
|
DeviceAuthCallback(TransportContext transportContext, DeferredResult<ResponseEntity> responseWriter, Consumer<SessionInfoProto> onSuccess) { |
|
|
|
this.transportContext = transportContext; |
|
|
|
this.responseWriter = responseWriter; |
|
|
|
this.onSuccess = onSuccess; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) { |
|
|
|
if (msg.hasDeviceInfo()) { |
|
|
|
UUID sessionId = UUID.randomUUID(); |
|
|
|
DeviceInfoProto deviceInfoProto = msg.getDeviceInfo(); |
|
|
|
SessionInfoProto sessionInfo = SessionInfoProto.newBuilder() |
|
|
|
.setNodeId(transportContext.getNodeId()) |
|
|
|
.setTenantIdMSB(deviceInfoProto.getTenantIdMSB()) |
|
|
|
.setTenantIdLSB(deviceInfoProto.getTenantIdLSB()) |
|
|
|
.setDeviceIdMSB(deviceInfoProto.getDeviceIdMSB()) |
|
|
|
.setDeviceIdLSB(deviceInfoProto.getDeviceIdLSB()) |
|
|
|
.setSessionIdMSB(sessionId.getMostSignificantBits()) |
|
|
|
.setSessionIdLSB(sessionId.getLeastSignificantBits()) |
|
|
|
.build(); |
|
|
|
onSuccess.accept(sessionInfo); |
|
|
|
} else { |
|
|
|
responseWriter.setResult(new ResponseEntity<>(HttpStatus.UNAUTHORIZED)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onError(Throwable e) { |
|
|
|
log.warn("Failed to process request", e); |
|
|
|
responseWriter.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private static class SessionCloseOnErrorCallback implements TransportServiceCallback<Void> { |
|
|
|
private final TransportService transportService; |
|
|
|
private final SessionInfoProto sessionInfo; |
|
|
|
|
|
|
|
SessionCloseOnErrorCallback(TransportService transportService, SessionInfoProto sessionInfo) { |
|
|
|
this.transportService = transportService; |
|
|
|
this.sessionInfo = sessionInfo; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onSuccess(Void msg) { |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onError(Throwable e) { |
|
|
|
transportService.deregisterSession(sessionInfo); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private static class HttpSessionListener implements SessionMsgListener { |
|
|
|
|
|
|
|
private final TransportContext transportContext; |
|
|
|
private final DeferredResult<ResponseEntity> responseWriter; |
|
|
|
|
|
|
|
HttpSessionListener(TransportContext transportContext, DeferredResult<ResponseEntity> responseWriter) { |
|
|
|
this.transportContext = transportContext; |
|
|
|
this.responseWriter = responseWriter; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onGetAttributesResponse(GetAttributeResponseMsg msg) { |
|
|
|
responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK)); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onAttributeUpdate(AttributeUpdateNotificationMsg msg) { |
|
|
|
responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK)); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onRemoteSessionCloseCommand(SessionCloseNotificationProto sessionCloseNotification) { |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) { |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse) { |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |