/* * Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license. * See LICENSE in the project root for license information. */ package com.linkedin.flashback.smartproxy.proxycontroller; import com.linkedin.flashback.SceneAccessLayer; import com.linkedin.flashback.netty.builder.RecordedHttpRequestBuilder; import com.linkedin.flashback.netty.mapper.NettyHttpResponseMapper; import com.linkedin.flashback.serializable.RecordedHttpRequest; import com.linkedin.flashback.serializable.RecordedHttpResponse; import com.linkedin.flashback.smartproxy.utils.NoMatchResponseGenerator; import com.linkedin.mitm.proxy.channel.ChannelMediator; import com.linkedin.mitm.proxy.dataflow.ProxyModeController; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.LastHttpContent; import java.io.IOException; import org.apache.log4j.Logger; /** * Replay controller which playback http response based on matched http request * New instance gets created for each new connection coming. * * @author shfeng */ public class ReplayController implements ProxyModeController { private static final Logger LOG = Logger.getLogger(ReplayController.class); private final RecordedHttpRequestBuilder _clientRequestBuilder; private final SceneAccessLayer _sceneAccessLayer; public ReplayController(SceneAccessLayer sceneAccessLayer, HttpRequest httpRequest) { _clientRequestBuilder = new RecordedHttpRequestBuilder(httpRequest); _sceneAccessLayer = sceneAccessLayer; } @Override public void handleReadFromClient(ChannelMediator channelMediator, HttpObject httpObject) { if (channelMediator == null) { throw new IllegalStateException("HRFC: ChannelMediator can't be null"); } try { if (httpObject instanceof HttpRequest) { HttpRequest httpRequest = (HttpRequest) httpObject; _clientRequestBuilder.interpretHttpRequest(httpRequest); _clientRequestBuilder.addHeaders(httpRequest); } if (httpObject instanceof HttpContent) { _clientRequestBuilder.appendHttpContent((HttpContent) httpObject); } if (httpObject instanceof LastHttpContent) { HttpResponse httpResponse = playBack(); channelMediator.writeToClientAndDisconnect(httpResponse); } } catch (IOException e) { throw new RuntimeException("HRFC: Failed to replay HttpContent", e); } } @Override public void handleReadFromServer(HttpObject httpObject) { throw new IllegalStateException("No read from server in replay mode"); } /** * If found matched request, then return response accordingly. * Otherwise, return bad request. * * @return bad request if not matched request/response found in the scene. * */ private FullHttpResponse playBack() throws IOException { RecordedHttpRequest recordedHttpRequest = _clientRequestBuilder.build(); boolean found = _sceneAccessLayer.hasMatchRequest(recordedHttpRequest); if (!found) { if (LOG.isDebugEnabled()) { LOG.debug(_sceneAccessLayer.getMatchFailureDescription(recordedHttpRequest)); } return NoMatchResponseGenerator.generateNoMatchResponse(recordedHttpRequest); } RecordedHttpResponse recordedHttpResponse = _sceneAccessLayer.playback(recordedHttpRequest); return NettyHttpResponseMapper.from(recordedHttpResponse); } }