/**
* Copyright 2016 LinkedIn Corp. All rights reserved.
*
* 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
*
* 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.
*/
package com.github.ambry.rest;
import com.github.ambry.router.AsyncWritableChannel;
import com.github.ambry.router.Callback;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.Future;
/**
* The RestResponseChannel is meant to provide a {@link NioServer} implementation independent way to return responses
* to the client. It deals with data in terms of bytes only and is not concerned with different types of data that might
* need to be returned from the {@link BlobStorageService}.
* <p/>
* This functionality is mostly required by implementations of {@link BlobStorageService} since they are agnostic to
* both the REST protocol being used and the framework used for the implementation of {@link NioServer}.
* <p/>
* Typically, the RestResponseChannel wraps the underlying network channel and the APIs of the NIO framework to return
* responses to clients.
* <p/>
* Implementations are expected to be thread-safe but use with care across different threads since there are neither
* ordering guarantees nor operation success guarantees (e.g. if an external thread closes the channel while a write
* attempt is in progress) - especially with concurrent writes.
*/
public interface RestResponseChannel extends AsyncWritableChannel {
/**
* Adds a sequence of bytes to the body of the response.
* <p/>
* If any write fails, all subsequent writes will fail.
* {@inheritDoc}
* @param src the data that needs to be written to the channel.
* @param callback the {@link Callback} that will be invoked once the write succeeds/fails. This can be null.
* @return a {@link Future} that will eventually contain the result of the write operation.
*/
@Override
public Future<Long> write(ByteBuffer src, Callback<Long> callback);
/**
* Closes the underlying network channel immediately. This should be used only if there is an error and the channel
* needs to be closed immediately.
* <p/>
* Typically {@link #onResponseComplete(Exception)} will take care of closing the channel if required. It will
* handle keep-alive headers and close the channel gracefully as opposed to this function which will simply close
* the channel abruptly.
* {@inheritDoc}
* @throws IOException if there was a problem closing the channel.
*/
@Override
public void close() throws IOException;
/**
* Notifies that response handling for the request is complete (whether the request succeeded or not) and tasks that
* need to be done after handling of a response is complete can proceed (e.g. cleanup code + closing of connection if
* not keepalive).
* <p/>
* If {@code exception} is not null, then it indicates that there was an error while handling the request and
* {@code exception} defines the error that occurred. The expectation is that an appropriate error response will be
* constructed, returned to the client if possible and the connection closed (if required).
* <p/>
* It is possible that the connection might be closed/severed before this is called. Therefore this function needs to
* always check if the channel of communication with the client is still open if it wants to send data.
* <p/>
* A response is considered to be complete either when a complete response has been sent to the client, an exception
* occurred while handling the request or if the client timed out (or there was any other client side error).
* <p/>
* A response of OK is returned if {@code exception} is null and no response body was constructed (i.e if there were no
* {@link #write(ByteBuffer, Callback)} calls).
* <p/>
* This is (has to be) called regardless of the request being concluded successfully or unsuccessfully
* (e.g. connection interruption).
* <p/>
* This operation is idempotent.
* @param exception if an error occurred, the cause of the error. Otherwise null.
*/
public void onResponseComplete(Exception exception);
/**
* Sets the response status.
* @param status the response status.
* @throws RestServiceException if there is an error setting the header.
*/
public void setStatus(ResponseStatus status) throws RestServiceException;
/**
* Gets the current {@link ResponseStatus}.
* @return the response status.
*/
public ResponseStatus getStatus();
/**
* Sets header {@code headerName} to {@code headerValue}.
* @param headerName the name of the header to set to {@code headerValue}.
* @param headerValue the value of the header with name {@code headerName}.
* @throws RestServiceException if there is an error setting the header.
*/
public void setHeader(String headerName, Object headerValue) throws RestServiceException;
/**
* Gets the current value of the header with {@code headerName}.
* @param headerName the name of the header whose value is required.
* @return the value of the header with name {@code headerName} if it exists. {@code null} otherwise.
*/
public Object getHeader(String headerName);
}