OpenSearch/src/main/java/org/elasticsearch/http/netty/NettyHttpChannel.java

271 lines
12 KiB
Java
Raw Normal View History

2010-02-08 15:30:06 +02:00
/*
2011-12-06 02:42:25 +02:00
* Licensed to ElasticSearch and Shay Banon under one
2010-02-08 15:30:06 +02:00
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
2011-12-06 02:42:25 +02:00
* regarding copyright ownership. ElasticSearch licenses this
2010-02-08 15:30:06 +02:00
* file to you 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. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.http.netty;
import org.elasticsearch.common.io.stream.CachedStreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder;
2010-02-08 15:30:06 +02:00
import org.elasticsearch.http.HttpChannel;
import org.elasticsearch.http.HttpException;
2010-02-17 09:28:06 +02:00
import org.elasticsearch.rest.RestResponse;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.rest.XContentRestResponse;
import org.elasticsearch.rest.support.RestUtils;
import org.elasticsearch.transport.netty.NettyTransport;
2011-12-06 02:42:25 +02:00
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.handler.codec.http.*;
2010-02-08 15:30:06 +02:00
import java.io.IOException;
import java.util.List;
import java.util.Map;
2010-02-08 15:30:06 +02:00
import java.util.Set;
/**
2011-12-06 02:42:25 +02:00
*
2010-02-08 15:30:06 +02:00
*/
public class NettyHttpChannel implements HttpChannel {
private final NettyHttpServerTransport transport;
2010-02-08 15:30:06 +02:00
private final Channel channel;
2011-12-06 02:42:25 +02:00
private final org.jboss.netty.handler.codec.http.HttpRequest request;
2010-02-08 15:30:06 +02:00
2011-12-06 02:42:25 +02:00
public NettyHttpChannel(NettyHttpServerTransport transport, Channel channel, org.jboss.netty.handler.codec.http.HttpRequest request) {
this.transport = transport;
2010-02-08 15:30:06 +02:00
this.channel = channel;
this.request = request;
}
2011-12-06 02:42:25 +02:00
@Override
public void sendResponse(RestResponse response) {
2010-02-08 15:30:06 +02:00
// Decide whether to close the connection or not.
boolean http10 = request.getProtocolVersion().equals(HttpVersion.HTTP_1_0);
boolean close =
HttpHeaders.Values.CLOSE.equalsIgnoreCase(request.getHeader(HttpHeaders.Names.CONNECTION)) ||
(http10 && !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request.getHeader(HttpHeaders.Names.CONNECTION)));
// Build the response object.
HttpResponseStatus status = getStatus(response.status());
2011-12-06 02:42:25 +02:00
org.jboss.netty.handler.codec.http.HttpResponse resp;
2010-02-08 15:30:06 +02:00
if (http10) {
resp = new DefaultHttpResponse(HttpVersion.HTTP_1_0, status);
if (!close) {
resp.addHeader(HttpHeaders.Names.CONNECTION, "Keep-Alive");
}
} else {
resp = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status);
}
if (RestUtils.isBrowser(request.getHeader(HttpHeaders.Names.USER_AGENT))) {
if (transport.settings().getAsBoolean("http.cors.enabled", true)) {
// Add support for cross-origin Ajax requests (CORS)
resp.addHeader("Access-Control-Allow-Origin", transport.settings().get("http.cors.allow-origin", "*"));
if (request.getMethod() == HttpMethod.OPTIONS) {
// Allow Ajax requests based on the CORS "preflight" request
resp.addHeader("Access-Control-Max-Age", transport.settings().getAsInt("http.cors.max-age", 1728000));
resp.addHeader("Access-Control-Allow-Methods", transport.settings().get("http.cors.allow-methods", "OPTIONS, HEAD, GET, POST, PUT, DELETE"));
resp.addHeader("Access-Control-Allow-Headers", transport.settings().get("http.cors.allow-headers", "X-Requested-With, Content-Type, Content-Length"));
}
}
}
String opaque = request.getHeader("X-Opaque-Id");
if (opaque != null) {
resp.addHeader("X-Opaque-Id", opaque);
}
// Add all custom headers
Map<String, List<String>> customHeaders = response.getHeaders();
if (customHeaders != null) {
for (Map.Entry<String, List<String>> headerEntry : customHeaders.entrySet()) {
for (String headerValue : headerEntry.getValue()) {
resp.addHeader(headerEntry.getKey(), headerValue);
}
}
}
2010-02-08 15:30:06 +02:00
// Convert the response content to a ChannelBuffer.
ChannelFutureListener releaseContentListener = null;
2010-02-08 15:30:06 +02:00
ChannelBuffer buf;
try {
if (response instanceof XContentRestResponse) {
// if its a builder based response, and it was created with a CachedStreamOutput, we can release it
// after we write the response, and no need to do an extra copy because its not thread safe
XContentBuilder builder = ((XContentRestResponse) response).builder();
if (builder.payload() instanceof CachedStreamOutput.Entry) {
releaseContentListener = new NettyTransport.CacheFutureListener((CachedStreamOutput.Entry) builder.payload());
buf = builder.bytes().toChannelBuffer();
} else if (response.contentThreadSafe()) {
buf = ChannelBuffers.wrappedBuffer(response.content(), response.contentOffset(), response.contentLength());
} else {
buf = ChannelBuffers.copiedBuffer(response.content(), response.contentOffset(), response.contentLength());
}
} else {
if (response.contentThreadSafe()) {
buf = ChannelBuffers.wrappedBuffer(response.content(), response.contentOffset(), response.contentLength());
} else {
buf = ChannelBuffers.copiedBuffer(response.content(), response.contentOffset(), response.contentLength());
}
}
} catch (IOException e) {
throw new HttpException("Failed to convert response to bytes", e);
2010-02-08 15:30:06 +02:00
}
if (response.prefixContent() != null || response.suffixContent() != null) {
ChannelBuffer prefixBuf = ChannelBuffers.EMPTY_BUFFER;
if (response.prefixContent() != null) {
prefixBuf = ChannelBuffers.copiedBuffer(response.prefixContent(), response.prefixContentOffset(), response.prefixContentLength());
2010-02-08 15:30:06 +02:00
}
ChannelBuffer suffixBuf = ChannelBuffers.EMPTY_BUFFER;
if (response.suffixContent() != null) {
suffixBuf = ChannelBuffers.copiedBuffer(response.suffixContent(), response.suffixContentOffset(), response.suffixContentLength());
2010-02-08 15:30:06 +02:00
}
buf = ChannelBuffers.wrappedBuffer(prefixBuf, buf, suffixBuf);
}
resp.setContent(buf);
resp.setHeader(HttpHeaders.Names.CONTENT_TYPE, response.contentType());
resp.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buf.readableBytes()));
if (transport.resetCookies) {
String cookieString = request.getHeader(HttpHeaders.Names.COOKIE);
if (cookieString != null) {
CookieDecoder cookieDecoder = new CookieDecoder();
Set<Cookie> cookies = cookieDecoder.decode(cookieString);
if (!cookies.isEmpty()) {
// Reset the cookies if necessary.
CookieEncoder cookieEncoder = new CookieEncoder(true);
for (Cookie cookie : cookies) {
cookieEncoder.addCookie(cookie);
}
resp.addHeader(HttpHeaders.Names.SET_COOKIE, cookieEncoder.encode());
2010-02-08 15:30:06 +02:00
}
}
}
// Write the response.
ChannelFuture future = channel.write(resp);
if (releaseContentListener != null) {
future.addListener(releaseContentListener);
}
2010-02-08 15:30:06 +02:00
// Close the connection after the write operation is done if necessary.
if (close) {
future.addListener(ChannelFutureListener.CLOSE);
}
}
private HttpResponseStatus getStatus(RestStatus status) {
2010-02-08 15:30:06 +02:00
switch (status) {
case CONTINUE:
return HttpResponseStatus.CONTINUE;
case SWITCHING_PROTOCOLS:
return HttpResponseStatus.SWITCHING_PROTOCOLS;
case OK:
return HttpResponseStatus.OK;
case CREATED:
return HttpResponseStatus.CREATED;
case ACCEPTED:
return HttpResponseStatus.ACCEPTED;
case NON_AUTHORITATIVE_INFORMATION:
return HttpResponseStatus.NON_AUTHORITATIVE_INFORMATION;
case NO_CONTENT:
return HttpResponseStatus.NO_CONTENT;
case RESET_CONTENT:
return HttpResponseStatus.RESET_CONTENT;
case PARTIAL_CONTENT:
return HttpResponseStatus.PARTIAL_CONTENT;
case MULTI_STATUS:
// no status for this??
return HttpResponseStatus.INTERNAL_SERVER_ERROR;
case MULTIPLE_CHOICES:
return HttpResponseStatus.MULTIPLE_CHOICES;
case MOVED_PERMANENTLY:
return HttpResponseStatus.MOVED_PERMANENTLY;
case FOUND:
return HttpResponseStatus.FOUND;
case SEE_OTHER:
return HttpResponseStatus.SEE_OTHER;
case NOT_MODIFIED:
return HttpResponseStatus.NOT_MODIFIED;
case USE_PROXY:
return HttpResponseStatus.USE_PROXY;
case TEMPORARY_REDIRECT:
return HttpResponseStatus.TEMPORARY_REDIRECT;
case BAD_REQUEST:
return HttpResponseStatus.BAD_REQUEST;
case UNAUTHORIZED:
2010-05-12 04:03:20 +03:00
return HttpResponseStatus.UNAUTHORIZED;
2010-02-08 15:30:06 +02:00
case PAYMENT_REQUIRED:
return HttpResponseStatus.PAYMENT_REQUIRED;
case FORBIDDEN:
return HttpResponseStatus.FORBIDDEN;
case NOT_FOUND:
return HttpResponseStatus.NOT_FOUND;
case METHOD_NOT_ALLOWED:
return HttpResponseStatus.METHOD_NOT_ALLOWED;
case NOT_ACCEPTABLE:
return HttpResponseStatus.NOT_ACCEPTABLE;
case PROXY_AUTHENTICATION:
return HttpResponseStatus.PROXY_AUTHENTICATION_REQUIRED;
case REQUEST_TIMEOUT:
return HttpResponseStatus.REQUEST_TIMEOUT;
case CONFLICT:
return HttpResponseStatus.CONFLICT;
case GONE:
return HttpResponseStatus.GONE;
case LENGTH_REQUIRED:
return HttpResponseStatus.LENGTH_REQUIRED;
case PRECONDITION_FAILED:
return HttpResponseStatus.PRECONDITION_FAILED;
case REQUEST_ENTITY_TOO_LARGE:
return HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE;
case REQUEST_URI_TOO_LONG:
return HttpResponseStatus.REQUEST_URI_TOO_LONG;
case UNSUPPORTED_MEDIA_TYPE:
return HttpResponseStatus.UNSUPPORTED_MEDIA_TYPE;
case REQUESTED_RANGE_NOT_SATISFIED:
return HttpResponseStatus.REQUESTED_RANGE_NOT_SATISFIABLE;
case EXPECTATION_FAILED:
return HttpResponseStatus.EXPECTATION_FAILED;
case UNPROCESSABLE_ENTITY:
return HttpResponseStatus.BAD_REQUEST;
case LOCKED:
return HttpResponseStatus.BAD_REQUEST;
case FAILED_DEPENDENCY:
return HttpResponseStatus.BAD_REQUEST;
case INTERNAL_SERVER_ERROR:
return HttpResponseStatus.INTERNAL_SERVER_ERROR;
case NOT_IMPLEMENTED:
return HttpResponseStatus.NOT_IMPLEMENTED;
case BAD_GATEWAY:
return HttpResponseStatus.BAD_GATEWAY;
case SERVICE_UNAVAILABLE:
return HttpResponseStatus.SERVICE_UNAVAILABLE;
case GATEWAY_TIMEOUT:
return HttpResponseStatus.GATEWAY_TIMEOUT;
case HTTP_VERSION_NOT_SUPPORTED:
return HttpResponseStatus.HTTP_VERSION_NOT_SUPPORTED;
default:
return HttpResponseStatus.INTERNAL_SERVER_ERROR;
}
}
}