@Slf4j
@Component
public class RequestValidator {
/** * Validate the request, including service type and parameters. * * @param information All information for the HTTP request * * @throws IllegalRequestException The request is an invalid request, such as a wrong service type, a lack of * parameters or a wrong parameters. */ public void validateRequest(AbstractRequestChannelInformation information) throws IllegalRequestException { if (information instanceof GetRequestChannelInformation) { GetRequestChannelInformation getChannelInformation = (GetRequestChannelInformation) information; Map<String, String> params = getChannelInformation.getParams(); validateRelayParams(params); validateRelayAuthContext(getChannelInformation.isFFmpegChannel() ? ChannelType.FFMPEG : ChannelType.SINK, params.get(ParamKey.TYPE), getChannelInformation.getAuthContext()); if (getChannelInformation.getChannelVersion() == ChannelVersion.VERSION_1_3) { ContentType contentType = getChannelInformation.getContentType(); if (contentType == null) { // ws建立不需要contentType if (ObjectUtil.equals(getChannelInformation.getStatisticInformation().getService(), RelayConsts.Service.WS_LIVE_VIEW_ESTABLISH)) { return; } throw new IllegalRequestException("POST request without contentType"); } else if (!contentType.isMultiPart()) { throw new IllegalRequestException("Sink channel of version 1.3 need multipart"); } } } else if (information instanceof PostRequestChannelInformation) { PostRequestChannelInformation postChannelInformation = (PostRequestChannelInformation) information; Map<String, String> params = postChannelInformation.getParams(); validateRelayParams(params); validateRelayAuthContext(ChannelType.SOURCE, params.get(ParamKey.TYPE), postChannelInformation.getAuthContext()); ContentType contentType = postChannelInformation.getContentType(); if (contentType == null) { throw new IllegalRequestException("POST request without contentType"); } else if (postChannelInformation.getChannelVersion() == ChannelVersion.VERSION_1_3 && !contentType.isMultiPart()) { throw new IllegalRequestException("Source channel of version 1.3 need multipart"); } } else if (information instanceof BidirectionRequestChannelInformation) { BidirectionRequestChannelInformation bidirectionChannelInformation = (BidirectionRequestChannelInformation) information; Map<String, String> params = bidirectionChannelInformation.getParams(); validateRelayParams(params); validateRelayAuthContext(ChannelType.BIDIRECTION, params.get(ParamKey.TYPE), bidirectionChannelInformation.getAuthContext()); ContentType contentType = bidirectionChannelInformation.getContentType(); if (contentType == null) { if (ObjectUtil.equals(bidirectionChannelInformation.getStatisticInformation().getService(), RelayConsts.Service.WS_LIVE_VIEW_ESTABLISH) || ObjectUtil .equals(bidirectionChannelInformation.getStatisticInformation().getService(), RelayConsts.Service.WS_PALYBACK_ESTABLISH)) { return; } throw new IllegalRequestException("POST request without contentType"); } else if (!contentType.isMultiPart()) { throw new IllegalRequestException("Bidirection channel of version 1.3 need multipart"); } } else if (information instanceof ControlRequestChannelInformation) { ControlRequestChannelInformation controlChannelInformation = (ControlRequestChannelInformation) information; Map<String, String> params = controlChannelInformation.getParams(); if (!params.containsKey(ParamKey.DEVICE_ID)) { throw new IllegalRequestException("Control request without device id"); } if (controlChannelInformation.getHttpMethod() != HttpMethod.OPTIONS) { ContentType contentType = controlChannelInformation.getContentType(); if (contentType == null) { throw new IllegalRequestException("POST request without contentType"); } else if (contentType.isMultiPart()) { throw new IllegalRequestException("control request should not be multipart"); } } } } private void validateRelayParams(Map<String, String> relayParams) { if (!relayParams.containsKey(ParamKey.DEVICE_ID)) { throw new IllegalRequestException("Relay request without device id"); } String streamType = relayParams.get(ParamKey.TYPE); if (streamType == null) { throw new IllegalRequestException("Relay request without type"); } validateStreamParams(streamType, relayParams); } public void validateRelayParams(Map<String, String> relayParams, String wantedDeviceId, String... wantedStreamTypes) { String paramDeviceId = relayParams.get(ParamKey.DEVICE_ID); if (!wantedDeviceId.equals(paramDeviceId)) { throw new IllegalRequestException("Wrong deviceId:" + paramDeviceId); } String paramStreamType = relayParams.get(ParamKey.TYPE); if (paramStreamType == null) { throw new IllegalRequestException("Relay service without type"); } if (wantedStreamTypes == null || wantedStreamTypes.length == 0) { throw new IllegalRequestException("Wrong type:" + paramStreamType); } for (String wantedType : wantedStreamTypes) { if (paramStreamType.equals(wantedType)) { validateStreamParams(paramStreamType, relayParams); return; } } throw new IllegalRequestException("Wrong type:" + paramStreamType); } private void validateStreamParams(String streamType, Map<String, String> params) { switch (streamType) { // VIDEO, MIXED, AUDIO均无需处理 case Type.VIDEO: case RelayConsts.Type.SMART_IPC: case Type.MIXED: case Type.AUDIO: // Resolution is only used as key to build binding id. Device and app have the same resolution will be bound // with each other. The known values of resolution are listed as follows: // 1. For video and mixed type: VGA, HD, QVGA // 2. For audio type: AAC, PCM, MP2 // 3. For nvr type: VGA, HD, QVGA or AAC, PCM, MP2 // When the resolution is missing, the binding key could be built without resolution. // When the resolution is other value, the resolution is valid. For nvr type, other resolution is regarded // as video resolution. break; case RelayConsts.Type.SMART_NVR: case Type.NVR: if (params.get(ParamKey.CHANNEL) == null) { throw new IllegalRequestException("Type NVR without channel"); } break; // SDVOD, DTSPK校验相同 case Type.SDVOD: case Type.DTSPK: if (params.get(ParamKey.PLAYERID) == null) { throw new IllegalRequestException("Sdvod or Dtspk without player id"); } break; case Type.FILE: if (params.get(ParamKey.FILE_ID) == null) { throw new IllegalRequestException("File without file id"); } break; case Type.NAS: if (params.get(ParamKey.CONNECTION_ID) == null) { throw new IllegalRequestException("Nas without connection id"); } break; case Type.DOWNLOAD: if (params.get(ParamKey.BIDIRECTION_ID) == null) { throw new IllegalRequestException("Download without bidirection id"); } break; default: throw new IllegalRequestException("Unresolved type of relay request:" + streamType); } } private void validateRelayAuthContext(ChannelType channelType, String streamType, AbstractAuthContext authContext) { assert streamType != null; switch (streamType) { case Type.VIDEO: case RelayConsts.Type.SMART_IPC: case Type.AUDIO: case Type.MIXED: case RelayConsts.Type.SMART_NVR: case Type.NVR: if (channelType == ChannelType.SOURCE || channelType == ChannelType.SINK) { validateAuthContext(authContext, BindingAuthContext.class); } else { throw new IllegalRequestException("Program should not be here, " + "streamType:" + streamType + " with channelType:" + channelType.name()); } break; case Type.SDVOD: case Type.DTSPK: case Type.FILE: case Type.DOWNLOAD: case Type.NAS: validateAuthContext(authContext, BindingAuthContext.class); break; default: throw new IllegalRequestException("Unresolved type of relay request:" + streamType); } } public void validateAuthContext(AbstractAuthContext authContext, Class<?>... wantedClasses) { assert wantedClasses != null && wantedClasses.length != 0; if (authContext == null) { throw new IllegalRequestException("Missing token"); } for (Class<?> wantedClass : wantedClasses) { if (wantedClass.isInstance(authContext)) { authContext.validateAuthParams(); return; } } log.error("Wrong auth context type:{}", authContext.getClass().getSimpleName()); throw new IllegalRequestException("Wrong auth context type:" + authContext.getClass().getSimpleName()); } enum ChannelType { /* * 通道类型 */ SOURCE, SINK, FFMPEG, BIDIRECTION; }
}
@Component
public class ProcessorTypeManager {
private ProcessorType statusProcessorType; private ProcessorType controlProcessorType; private ProcessorType wsControlProcessorType; private ProcessorType optionProcessorType; private ProcessorType passthroughGetServiceProcessorType; private ProcessorType defaultGetServiceProcessorType; private ProcessorType bufferedPostServiceProcessorType; private ProcessorType defaultPostServiceProcessorType; private ProcessorType bidirectionServiceProcessorType; private ProcessorType trustCertProcessorType; @Autowired public ProcessorTypeManager(DefaultHeaderProcessor defaultHeaderProcessor, ControlHeaderProcessor controlHeaderProcessor, ControlDataProcessor controlDataProcessor, WsControlDataProcessor wsControlDataProcessor, GetRequestHeaderProcessor defaultGetServiceHeaderProcessor, PostRequestHeaderProcessor defaultPostServiceHeaderProcessor, BidirectionRequestHeaderProcessor bidirectionServiceHeaderProcessor, PassthroughGetRequestDataProcessor passthroughGetServiceDataProcessor, BufferedRequestDataProcessor bufferedPostServiceDataProcessor, TrustCertProcessor trustCertProcessor, @Qualifier("relayPostRequestDataProcessor") RelayPostRequestDataProcessor defaultPostServiceDataProcessor, @Qualifier("postRequestDataProcessor") PostRequestDataProcessor bidirectionServiceDataProcessor) { statusProcessorType = new ProcessorType(Service.STATUS, defaultHeaderProcessor, null); optionProcessorType = new ProcessorType(Service.CONTROL, defaultHeaderProcessor, null); controlProcessorType = new ProcessorType(Service.CONTROL, controlHeaderProcessor, controlDataProcessor); wsControlProcessorType = new ProcessorType(Service.CONTROL, controlHeaderProcessor, wsControlDataProcessor); passthroughGetServiceProcessorType = new ProcessorType(Service.RELAY, defaultGetServiceHeaderProcessor, passthroughGetServiceDataProcessor); defaultGetServiceProcessorType = new ProcessorType(Service.RELAY, defaultGetServiceHeaderProcessor, null); bufferedPostServiceProcessorType = new ProcessorType(Service.RELAY, defaultPostServiceHeaderProcessor, bufferedPostServiceDataProcessor); defaultPostServiceProcessorType = new ProcessorType(Service.RELAY, defaultPostServiceHeaderProcessor, defaultPostServiceDataProcessor); bidirectionServiceProcessorType = new ProcessorType(Service.RELAY, bidirectionServiceHeaderProcessor, bidirectionServiceDataProcessor); trustCertProcessorType = new ProcessorType(Service.TRUST_CERT, trustCertProcessor, null); } public ProcessorType getProcessorType(RequestChannel<? extends AbstractRequestChannelInformation> requestChannel) { if (requestChannel instanceof BidirectionRequestChannel) { return bidirectionServiceProcessorType; } else if (requestChannel instanceof PassthroughGetRequestChannel) { return passthroughGetServiceProcessorType; } else if (requestChannel instanceof GetRequestChannel) { return defaultGetServiceProcessorType; } else if (requestChannel instanceof BufferedPostRequestChannel) { return bufferedPostServiceProcessorType; } else if (requestChannel instanceof PostRequestChannel) { return defaultPostServiceProcessorType; } else if (requestChannel instanceof ControlRequestChannel) { if (requestChannel.getInformation().getHttpMethod() == HttpMethod.OPTIONS) { return optionProcessorType; } return controlProcessorType; } else if (requestChannel instanceof StatusRequestChannel) { return statusProcessorType; } else if (requestChannel instanceof TrustCertChannel) { return trustCertProcessorType; } else { throw new UnexpectedServiceException(requestChannel.getRequestType()); } } public ProcessorType getWsControlProcessorType() { return wsControlProcessorType; }
}
@Component
public class DefaultHeaderProcessor extends
AbstractHeaderProcessor<RequestChannel, StatusRequestChannelInformation> {
@Autowired private CookieLoader cookieLoader; @Override protected RequestChannel<StatusRequestChannelInformation> doProcess( RequestChannel<StatusRequestChannelInformation> requestChannel) throws Exception { ResponseBuilder builder = ResponseBuilder.create(); builder.setServer("relayd"); builder.setStatus(HttpResponseStatus.OK).setEmptyContent(); String setCookie = cookieLoader.getSetCookieFieldValue(); if (setCookie != null) { builder.setCatchControl("no-cache=\"set-cookie\""); builder.addHeader("Set-Cookie", setCookie); } requestChannel.sendHttpResponseAndClose(builder.build(), RelayConsts.CloseReason.SUCCESS); return requestChannel; }
}
@Component
public class ControlHeaderProcessor extends
AbstractHeaderProcessor<ControlRequestChannel, ControlRequestChannelInformation> {
private ChannelManager channelManager; @Autowired public ControlHeaderProcessor(ChannelManager channelManager) { this.channelManager = channelManager; } @Override protected ControlRequestChannel doProcess(ControlRequestChannel requestChannel) { requestChannel.createLifeTimeEvent(ILifeTimeControl.LifeTimeType.BASIC); if (requestChannel.getInformation().getHttpMethod() != HttpMethod.OPTIONS) { requestChannel.setStartDataProcess(); } channelManager.addRequestChannel(requestChannel.getChannelId(), requestChannel); return requestChannel; }
}
@Slf4j
@Component
public class GetRequestHeaderProcessor extends
AuthRequiredHeaderProcessor<GetRequestChannel, GetRequestChannelInformation> {
private ChannelManager channelManager; private ContainerManager containerManager; private RequestChannelHelper requestChannelHelper; private HttpResponse upgradeHttpResponse; private ByteBuf upgradeVideo; private long upgradeVideoDurationMs; public GetRequestHeaderProcessor(AuthClient authClient, GetRequestHeaderProcessorProp prop, ChannelManager channelManager, ContainerManager containerManager, RequestChannelHelper requestChannelHelper) { super(authClient, prop); this.channelManager = channelManager; this.containerManager = containerManager; this.requestChannelHelper = requestChannelHelper; ResponseBuilder builder = ResponseBuilder.create(); builder.setStatus(HttpResponseStatus.OK).setContentType(new ContentType("video/mp2t", null, null)) .setContentLength(Long.MAX_VALUE); this.upgradeHttpResponse = builder.build(); } @Override protected GetRequestChannel doBeforeAddExecutor(GetRequestChannel getChannel) throws Exception { if (getChannel instanceof IDataFollowedRelayRequestChannel) { // Version 1.3 if (log.isDebugEnabled()) { log.debug("[sid:{}] Disable auto read of GET channel:", getChannel.getSid()); } if (BooleanUtils.isFalse(getChannel.getWs())) { ((IDataFollowedRelayRequestChannel) getChannel).disableAutoRead(); } } return getChannel; } @Override protected GetRequestChannel doProcess(GetRequestChannel getChannel) throws Exception { super.doProcess(getChannel); return getChannel; } @Override public void doHeaderProcess(GetRequestChannel getChannel) throws Exception { // Add GET request channel into map manager channelManager.addRequestChannel(getChannel.getChannelId(), getChannel); if (!getChannel.isNeedMultipleMappingSource() && containerManager.removeBidirectionContainerIfExist(getChannel)) { GetRequestChannelInformation information = getChannel.getInformation(); information.setVersionUnmatch(true); // Version 1.2, single mapping and source channel is Version 1.3 if (RelayConsts.Type.DTSPK.equals(information.getParams().get(RelayConsts.ParamKey.TYPE))) { // The DTSPK Get channel of version 1.2 is device, so it is not necessary to show upgraded message. And // as the app should check device version before request, this case should not happen. getChannel.sendHttpResponseAndClose(HttpResponseStatus.BAD_REQUEST, RelayConsts.CloseReason.GET_CLOSED_WITH_LOW_VERSION); } else { // For example, sdvod with 1.2 version showUpgradeHint(getChannel); } return; } // Get and bind corresponding POST request channel of this GET request channel PostRequestChannel postChannel = obtainPostRequestChannel(getChannel); if (postChannel == null) { if (getChannel instanceof DefaultGetRequestChannel) { addRequestChannelIntoContainer((DefaultGetRequestChannel) getChannel); } else { if (log.isDebugEnabled()) { log.debug("[sid:{}] Response 404 to the GET channel which is not life-time-controllable", getChannel.getSid()); } getChannel.sendHttpResponseAndClose(HttpResponseStatus.NOT_FOUND, RelayConsts.CloseReason.NOT_FOUND); } } else { if (postChannel.getChannelVersion() == getChannel.getChannelVersion()) { // POST and GET channel has the same version bindRequestChannels(getChannel, postChannel); } else if (getChannel.getChannelVersion() == ChannelVersion.VERSION_1_2) { // Multiple mapping and GET channel version is 1.2 and show hint video postChannel.getInformation().addVersionUnmatchNum(); getChannel.getInformation().setVersionUnmatch(true); showUpgradeHint(getChannel); } else { // GET channel version is 1.3. This case should not happen as following reason: // 1. For video/nvr (multiple mapping): the app will check the device version before requesting stream. // 2. For sdvod: the app will check the device version before requesting stream. // 3. For dtspk: this is used on IPC. When the user wants to speak, 2 connections will be used, one is // preview stream(type=video) and the other one is the dtspk stream.In this case, the version of device // is 1.3 and the one of app is 1.2. The hint will be shown through preview stream and the dtspk request // will be refused by device. postChannel.getInformation().addVersionUnmatchNum(); getChannel.getInformation().setVersionUnmatch(true); getChannel.sendHttpResponseAndClose(HttpResponseStatus.BAD_REQUEST, RelayConsts.CloseReason.GET_CLOSED_WITH_HIGHT_VERSION); } } } private PostRequestChannel obtainPostRequestChannel(GetRequestChannel getChannel) { // Obtain POST channel from manager directly: // 1. For single mapping: the result is the actual POST channel. // 2. For multiple mapping: if the result is null, the device may be a single stream output IP and should be // tried with shortcut binding id. If the result is not null, the result is the actual POST channel. String bindingId = getChannel.getBindingId(); PostRequestChannel postChannel = (PostRequestChannel) channelManager.getRequestChannel(bindingId); if (postChannel != null || !getChannel.isNeedMultipleMappingSource()) { return postChannel; } Map<String, String> params = getChannel.getInformation().getParams(); String shortBindingId = BindingIdUtils.buildMultipleMappingShortBindingId(params); bindingId = channelManager.getBindingIdByShortcut(shortBindingId); if (bindingId != null) { postChannel = (PostRequestChannel) channelManager.getRequestChannel(bindingId); } if (postChannel != null) { if (log.isDebugEnabled()) { log.debug("[sid:{}] Update binding id: newBindingId={}", getChannel.getSid(), bindingId); } getChannel.updateBindingId(bindingId); } return postChannel; } private void bindRequestChannels(GetRequestChannel getChannel, PostRequestChannel postChannel) throws Exception { BindingResult bindingResult = postChannel.bindGetRequestChannel(getChannel); if (bindingResult == BindingResult.SUCCESS) { if (log.isDebugEnabled()) { boolean inSameEventLoop = postChannel.getNettyChannel().eventLoop().inEventLoop(); log.debug("[sid:{}] Bind to POST channel successfully: inSameEventLoop={}", getChannel.getSid(), inSameEventLoop); } postChannel.startDataAcceptanceAfterSinkChannelAccess(); } else { log.error("[sid:{}] Fail to bind GET channel: bindingResult={}", getChannel.getSid(), bindingResult); getChannel.sendHttpResponseAndClose(bindingResult.getResponseStatus(), bindingResult.getResponseCloseReason()); } } private void addRequestChannelIntoContainer(DefaultGetRequestChannel getChannel) throws Exception { // Set idle life time event. getChannel.createLifeTimeEvent(ILifeTimeControl.LifeTimeType.CON_IDLE); // Add itself into container. ContainerManager.AddSingleDirectionContainerResult addContainerResult = containerManager.addIntoSingleDirectionContainer(getChannel); BindingResult bindingResult = addContainerResult.getBindingResult(); if (bindingResult == BindingResult.SUCCESS) { if (log.isDebugEnabled()) { log.debug("[sid:{}] Add GET channel into container successfully: container={}", getChannel.getSid(), addContainerResult.getContainer().toString()); } // If the version of POST and GET channel don't match with each other, the GET channel will be close when // initContainer is called. Especially for multiple mapping channels. PostRequestChannel postChannel = addContainerResult.getContainer().getSourceRequestChannel(); if (postChannel != null && postChannel.getBoundGetRequestChannelNum() != 0) { if (log.isDebugEnabled()) { log.debug("[sid:{}] Initialize POST channel after GET access", getChannel.getSid()); } postChannel.startDataAcceptanceAfterSinkChannelAccess(); } } else { log.error("[sid:{}] Fail to add GET channel into container: bindingResult:{}", getChannel.getSid(), bindingResult); getChannel.sendHttpResponseAndClose(bindingResult.getResponseStatus(), bindingResult.getResponseCloseReason()); } } private void showUpgradeHint(GetRequestChannel getChannel) { if (getChannel instanceof DefaultGetRequestChannel) { ((DefaultGetRequestChannel) getChannel).createLifeTimeEvent(ILifeTimeControl.LifeTimeType.BASIC); } getChannel.sendTemplateHttpResponse(upgradeHttpResponse); IHttpData firstData = new ByteBufHttpData(0, UnpooledByteBufAllocator.DEFAULT, CharsetUtil.UTF_8, upgradeVideo); getChannel.sendTemplateData(firstData); ShowUpgradeHintTask task = new ShowUpgradeHintTask(getChannel, 1); getChannel.getNettyChannel().eventLoop().schedule(task, upgradeVideoDurationMs, TimeUnit.MILLISECONDS); } public void setUpgradeVideo(String pathname) throws Exception { FileInputStream in = null; try { in = new FileInputStream(pathname); ByteBuf buf = Unpooled.buffer(); byte[] bytes = new byte[8192]; int readSize = -1; while ((readSize = in.read(bytes)) != -1) { buf.writeBytes(bytes, 0, readSize); } upgradeVideo = Unpooled.unreleasableBuffer(buf); } finally { if (in != null) { try { in.close(); } catch (IOException e) { log.warn(e.getMessage(), e); } } } } public void setUpgradeVideoDurationMs(long upgradeVideoDurationMs) { this.upgradeVideoDurationMs = upgradeVideoDurationMs; } class ShowUpgradeHintTask implements Runnable { GetRequestChannel getChannel; int dataIndex; ShowUpgradeHintTask(GetRequestChannel getChannel, int startDataIndex) { this.getChannel = getChannel; this.dataIndex = startDataIndex; } @Override public void run() { if (getChannel.isClosed()) { log.info("[sid:{}] Show hint task is stopped: finalDataIndex={}", getChannel.getSid(), dataIndex - 1); return; } getChannel.sendTemplateData(new ByteBufHttpData(dataIndex++, UnpooledByteBufAllocator.DEFAULT, CharsetUtil.UTF_8, upgradeVideo)); getChannel.getNettyChannel().eventLoop().schedule(this, upgradeVideoDurationMs, TimeUnit.MILLISECONDS); } }
}
@Slf4j
@Component
public class PostRequestHeaderProcessor extends
AuthRequiredHeaderProcessor<PostRequestChannel, PostRequestChannelInformation> {
private ChannelManager channelManager; private ContainerManager containerManager; private RequestChannelHelper requestChannelHelper; private CloudAccessChannelHelper cloudAccessChannelHelper; @Autowired public PostRequestHeaderProcessor(AuthClient authClient, PostRequestHeaderProcessorProp prop, ChannelManager channelManager, ContainerManager containerManager, RequestChannelHelper requestChannelHelper, CloudAccessChannelHelper cloudAccessChannelHelper) { super(authClient, prop); this.channelManager = channelManager; this.containerManager = containerManager; this.requestChannelHelper = requestChannelHelper; this.cloudAccessChannelHelper = cloudAccessChannelHelper; } @Override protected PostRequestChannel doBeforeAddExecutor(PostRequestChannel postChannel) throws Exception { if (log.isDebugEnabled()) { log.debug("[sid:{}] Disable auto read of POST channel:", postChannel.getSid()); } postChannel.disableAutoRead(); return postChannel; } /** * General POST request added strategy is to use new POST request to kick off the old one and all GET requests * related with the old POST request will be closed . * <p> * This method could be overrided for other strategy in specific transactions. * </p> * * @param postChannel The POST request channel. */ @Override public void doHeaderProcess(final PostRequestChannel postChannel) throws Exception { // Obtain the container. SingleDirectionContainer container = containerManager.obtainSingleDirectionContainer(postChannel); if (container == null) { log.info("[sid:{}] POST channel is kicked off by others when obtaining container.", postChannel.getSid()); postChannel.sendHttpResponseAndClose(503, "Kicked off", RelayConsts.CloseReason.POST_KICKED_OFF); return; } if (postChannel instanceof CloudAccessPostRequestChannel) { doCloudAccessHeaderProcess(postChannel, container); } // Initialize the container. log.debug("[sid:{}] POST channel obtain container successfully: container={}", postChannel.getSid(), container); boolean hasGetChannels = postChannel.initContainer(container); if (hasGetChannels) { log.debug("[sid:{}] Initialize POST channel after container initailization", postChannel.getSid()); postChannel.startDataAcceptanceAfterSinkChannelAccess(); } // Add binding id shortcut if necessary PostRequestChannel oldPostChannel = null; if (postChannel.isSingleStream()) { log.debug("[sid:{}] Add binding id shortcut: bindingId={}", postChannel.getSid(), postChannel.getBindingId()); String oldBindingId = channelManager.addBindingIdShortcut(postChannel.getShortBindingId(), postChannel.getBindingId()); if (oldBindingId != null) { oldPostChannel = (PostRequestChannel) channelManager.removeRequestChannel(oldBindingId); } if (oldPostChannel != null) { log.debug("[sid:{}] Kick off old channel: oldChannelSid={}", postChannel.getSid(), oldPostChannel.getSid()); oldPostChannel.close(RelayConsts.CloseReason.POST_KICKED_OFF); } } // Add POST channel into channel manager. oldPostChannel = (PostRequestChannel) channelManager.addRequestChannel(postChannel.getChannelId(), postChannel); if (oldPostChannel != null) { log.debug("[sid:{}] Kick off old channel: oldChannelSid={}", postChannel.getSid(), oldPostChannel.getSid()); oldPostChannel.close(RelayConsts.CloseReason.POST_KICKED_OFF); } // Process after adding into channel manager postChannel.callAfterAddIntoChannelManager(); } /** * 处理PostRequestChannel的cloud access推流 */ public void doCloudAccessHeaderProcess(final PostRequestChannel postChannel, SingleDirectionContainer container) throws Exception { RequestChannel cloudAccessChannel = cloudAccessChannelHelper .createCloudAccessChannel(postChannel.getBindingId(), postChannel.getInformation().getOrignalRequest(), postChannel.getNettyChannel()); if (cloudAccessChannel instanceof CloudAccessGetRequestChannel) { CloudAccessGetRequestChannel getChannel = (CloudAccessGetRequestChannel) cloudAccessChannel; channelManager.addRequestChannel(getChannel.getChannelId(), getChannel); // Set idle life time event. getChannel.createLifeTimeEvent(ILifeTimeControl.LifeTimeType.CON_IDLE); // Add itself into container. BindingResult bindingResult = container.addBoundSinkRequestChannel(getChannel); if (bindingResult == BindingResult.SUCCESS) { if (log.isDebugEnabled()) { log.debug("[sid:{}] Add GET channel into container successfully: container={}", getChannel.getSid(), container.toString()); } } else { log.error("[sid:{}] Fail to add GET channel into container: bindingResult:{}", getChannel.getSid(), bindingResult); getChannel.close(bindingResult.getResponseCloseReason()); } } else { log.error("failed to create cloud access channel for {}", postChannel.getBindingId()); } }
}