/* * Copyright (c) 2014 Globo.com - ATeam * All rights reserved. * * This source is subject to the Apache License, Version 2.0. * Please see the LICENSE file for more information. * * Authors: See AUTHORS file * * 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 com.globo.galeb.handlers; import com.globo.galeb.bus.IQueueService; import com.globo.galeb.bus.NullQueueService; import com.globo.galeb.entity.EntitiesMap; import com.globo.galeb.entity.impl.backend.BackendSession; import com.globo.galeb.entity.impl.backend.IBackend; import com.globo.galeb.entity.impl.backend.NullBackend; import com.globo.galeb.exceptions.ServiceUnavailableException; import com.globo.galeb.logger.SafeLogger; import com.globo.galeb.metrics.CounterConsoleOut; import com.globo.galeb.metrics.ICounter; import com.globo.galeb.request.RemoteUser; import com.globo.galeb.scheduler.IScheduler; import com.globo.galeb.scheduler.impl.NullScheduler; import com.globo.galeb.server.ServerResponse; import com.globo.galeb.streams.Pump; import org.vertx.java.core.Handler; import org.vertx.java.core.VoidHandler; import org.vertx.java.core.http.HttpClientResponse; import org.vertx.java.core.http.HttpServerResponse; /** * Class RouterResponseHandler. * * @author: See AUTHORS file. * @version: 1.0.0, Oct 23, 2014. */ public class RouterResponseHandler implements Handler<HttpClientResponse> { /** the scheduler instance */ private IScheduler scheduler = new NullScheduler(); /** The http server response. */ private HttpServerResponse httpServerResponse = null; /** The backend. */ private IBackend backend = new NullBackend(); /** The counter. */ private ICounter counter = new CounterConsoleOut(); /** The safelog. */ private SafeLogger log = null; /** The queue service. */ private IQueueService queueService = new NullQueueService(); /** The header host. */ private String headerHost = "UNDEF"; /** The initial request time. */ private Long initialRequestTime = null; /** The connection keepalive. */ private boolean connectionKeepalive = true; /** The remote user. */ private RemoteUser remoteUser = new RemoteUser(); /** The server response instance. */ private ServerResponse sResponse = null; /** * Define logger if necessary. */ private void defineLoggerIfNecessary() { if (log==null) { log = new SafeLogger(); } } /* (non-Javadoc) * @see org.vertx.java.core.Handler#handle(java.lang.Object) */ @Override public void handle(final HttpClientResponse cResponse) throws RuntimeException { defineLoggerIfNecessary(); if (sResponse==null||httpServerResponse==null) { log.error("Response is NULL"); return; } log.debug(String.format("Received response from backend %d %s", cResponse.statusCode(), cResponse.statusMessage())); scheduler.cancel(); updateResponseHeadersAndStatus(cResponse.statusCode(), cResponse); pumpStream(cResponse); } /** * Adjust status and header response. * * @param statusCode the status code * @param httpClientResponse the http client response */ private void updateResponseHeadersAndStatus(int statusCode, final HttpClientResponse httpClientResponse) { sResponse.setStatusCode(statusCode); sResponse.setHeaders(httpClientResponse.headers()); if (!connectionKeepalive) { httpServerResponse.headers().set("Connection", "close"); } } /** * Pump stream. * * @param httpClientResponse the http client response */ private void pumpStream(final HttpClientResponse httpClientResponse) { final Pump pump = new Pump(httpClientResponse, httpServerResponse); pump.exceptionHandler(new Handler<Throwable>() { @Override public void handle(Throwable throwable) { defineLoggerIfNecessary(); scheduler.cancel(); log.error(String.format("FAIL: RouterResponse.pump with %s", throwable.getMessage())); sResponse.showErrorAndClose(new ServiceUnavailableException()); } }); pump.writeHandler(new Handler<Void>() { @Override public void handle(Void v) { scheduler.cancel(); pump.writeHandler(null); } }); pump.start(); httpClientResponse.endHandler(new VoidHandler() { @Override public void handle() { String backendId = backend.toString(); if (!"UNDEF".equals(headerHost) && initialRequestTime!=null) { counter.requestTime(headerHost, backendId, initialRequestTime); } sResponse.setBackendId(backendId).endResponse(); defineLoggerIfNecessary(); if (!connectionKeepalive) { sResponse.closeResponse(); try { backend.close(remoteUser.toString()); } catch (RuntimeException e) { log.debug(e.getMessage()); } } log.debug(String.format("Completed backend response. %d bytes", pump.bytesPumped())); } }); httpClientResponse.exceptionHandler(new Handler<Throwable>() { @SuppressWarnings("unchecked") @Override public void handle(Throwable event) { String backendId = backend.toString(); defineLoggerIfNecessary(); log.error(String.format("host: %s , backend: %s , message: %s", headerHost, backendId, event.getMessage())); queueService.publishBackendFail(backend.toJson()); sResponse.setBackendId(backendId).showErrorAndClose(event); ((EntitiesMap<BackendSession>) backend).removeEntity(remoteUser.toString()); } }); } /** * Sets the header host. * * @param headerHost the header host * @return this */ public RouterResponseHandler setHeaderHost(String headerHost) { this.headerHost = headerHost; return this; } /** * Sets the initial request time. * * @param initialRequestTime the initial request time * @return this */ public RouterResponseHandler setInitialRequestTime(Long initialRequestTime) { this.initialRequestTime = initialRequestTime; return this; } /** * Sets the connection keepalive. * * @param connectionKeepalive the connection keepalive * @return this */ public RouterResponseHandler setConnectionKeepalive(boolean connectionKeepalive) { this.connectionKeepalive = connectionKeepalive; return this; } /** * Sets the scheduler. * * @param scheduler the scheduler * @return this */ public RouterResponseHandler setScheduler(final IScheduler scheduler) { this.scheduler = scheduler; return this; } /** * Sets the http server response. * * @param httpServerResponse the http server response * @return this */ public RouterResponseHandler setHttpServerResponse(final HttpServerResponse httpServerResponse) { this.httpServerResponse = httpServerResponse; return this; } /** * Sets response. * * @param sResponse the s response * @return this */ public RouterResponseHandler setsResponse(final ServerResponse sResponse) { this.sResponse = sResponse; return this; } /** * Sets the backend. * * @param backend the backend * @return this */ public RouterResponseHandler setBackend(final IBackend backend) { this.backend = backend; return this; } /** * Sets the counter. * * @param counter the counter * @return this */ public RouterResponseHandler setCounter(final ICounter counter) { this.counter = counter; return this; } /** * Sets the log. * * @param log the log * @return this */ public RouterResponseHandler setLog(final SafeLogger alog) { this.log = alog; return this; } /** * Sets the queue service. * * @param queueService the queue service * @return this */ public RouterResponseHandler setQueueService(final IQueueService queueService) { this.queueService = queueService; return this; } /** * Sets the remote user. * * @param remoteUser the remote user * @return this */ public RouterResponseHandler setRemoteUser(final RemoteUser remoteUser) { this.remoteUser = remoteUser; return this; } }