diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java index 54ecd0218e1..e02485b643d 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java @@ -55,13 +55,18 @@ public class HttpGenerator private long _contentPrepared = 0; private boolean _noContent = false; private Boolean _persistent = null; - private boolean _sendServerVersion; + + private final int _send; + private final static int SEND_SERVER=0x01; + private final static int SEND_XPOWEREDBY=0x02; /* ------------------------------------------------------------------------------- */ public static void setServerVersion(String version) { - SERVER=StringUtil.getBytes("Server: Jetty("+version+")\015\012"); + SEND[SEND_SERVER]=StringUtil.getBytes("Server: Jetty("+version+")\015\012"); + SEND[SEND_XPOWEREDBY]=StringUtil.getBytes("X-Powered-By: Jetty("+version+")\015\012"); + SEND[SEND_SERVER|SEND_XPOWEREDBY]=StringUtil.getBytes("Server: Jetty("+version+")\015\012X-Powered-By: Jetty("+version+")\015\012"); } /* ------------------------------------------------------------------------------- */ @@ -71,6 +76,13 @@ public class HttpGenerator /* ------------------------------------------------------------------------------- */ public HttpGenerator() { + this(false,false); + } + + /* ------------------------------------------------------------------------------- */ + public HttpGenerator(boolean sendServerVersion,boolean sendXPoweredBy) + { + _send=(sendServerVersion?SEND_SERVER:0) | (sendXPoweredBy?SEND_XPOWEREDBY:0); } /* ------------------------------------------------------------------------------- */ @@ -86,15 +98,17 @@ public class HttpGenerator } /* ------------------------------------------------------------ */ + @Deprecated public boolean getSendServerVersion () { - return _sendServerVersion; + return (_send&SEND_SERVER)!=0; } /* ------------------------------------------------------------ */ + @Deprecated public void setSendServerVersion (boolean sendServerVersion) { - _sendServerVersion = sendServerVersion; + throw new UnsupportedOperationException(); } /* ------------------------------------------------------------ */ @@ -537,11 +551,11 @@ public class HttpGenerator /* ------------------------------------------------------------ */ private void generateHeaders(Info _info,ByteBuffer header,ByteBuffer content,boolean last) { - final RequestInfo _request=(_info instanceof RequestInfo)?(RequestInfo)_info:null; - final ResponseInfo _response=(_info instanceof ResponseInfo)?(ResponseInfo)_info:null; + final RequestInfo request=(_info instanceof RequestInfo)?(RequestInfo)_info:null; + final ResponseInfo response=(_info instanceof ResponseInfo)?(ResponseInfo)_info:null; // default field values - boolean has_server = false; + int send=_send; HttpField transfer_encoding=null; boolean keep_alive=false; boolean close=false; @@ -584,7 +598,7 @@ public class HttpGenerator case CONNECTION: { - if (_request!=null) + if (request!=null) field.putTo(header); // Lookup and/or split connection value field @@ -619,7 +633,7 @@ public class HttpGenerator case CLOSE: { close=true; - if (_response!=null) + if (response!=null) { _persistent=false; if (_endOfContent == EndOfContent.UNKNOWN_CONTENT) @@ -633,7 +647,7 @@ public class HttpGenerator if (_info.getHttpVersion() == HttpVersion.HTTP_1_0) { keep_alive = true; - if (_response!=null) + if (response!=null) _persistent=true; } break; @@ -656,11 +670,8 @@ public class HttpGenerator case SERVER: { - if (getSendServerVersion()) - { - has_server=true; - field.putTo(header); - } + send=send&~SEND_SERVER; + field.putTo(header); break; } @@ -680,7 +691,7 @@ public class HttpGenerator // 4. Content-Length // 5. multipart/byteranges // 6. close - int status=_response!=null?_response.getStatus():-1; + int status=response!=null?response.getStatus():-1; switch (_endOfContent) { case UNKNOWN_CONTENT: @@ -688,14 +699,14 @@ public class HttpGenerator // written yet? // Response known not to have a body - if (_contentPrepared == 0 && _response!=null && (status < 200 || status == 204 || status == 304)) + if (_contentPrepared == 0 && response!=null && (status < 200 || status == 204 || status == 304)) _endOfContent=EndOfContent.NO_CONTENT; else if (_info.getContentLength()>0) { // we have been given a content length _endOfContent=EndOfContent.CONTENT_LENGTH; long content_length = _info.getContentLength(); - if ((_response!=null || content_length>0 || content_type ) && !_noContent) + if ((response!=null || content_length>0 || content_type ) && !_noContent) { // known length but not actually set. header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace()); @@ -710,7 +721,7 @@ public class HttpGenerator long content_length = _contentPrepared+BufferUtil.length(content); // Do we need to tell the headers about it - if ((_response!=null || content_length>0 || content_type ) && !_noContent) + if ((response!=null || content_length>0 || content_type ) && !_noContent) { header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace()); BufferUtil.putDecLong(header, content_length); @@ -721,7 +732,7 @@ public class HttpGenerator { // No idea, so we must assume that a body is coming _endOfContent = (!isPersistent() || _info.getHttpVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal() ) ? EndOfContent.EOF_CONTENT : EndOfContent.CHUNKED_CONTENT; - if (_response!=null && _endOfContent==EndOfContent.EOF_CONTENT) + if (response!=null && _endOfContent==EndOfContent.EOF_CONTENT) { _endOfContent=EndOfContent.NO_CONTENT; _noContent=true; @@ -731,7 +742,7 @@ public class HttpGenerator case CONTENT_LENGTH: long content_length = _info.getContentLength(); - if ((_response!=null || content_length>0 || content_type ) && !_noContent) + if ((response!=null || content_length>0 || content_type ) && !_noContent) { // known length but not actually set. header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace()); @@ -741,12 +752,12 @@ public class HttpGenerator break; case NO_CONTENT: - if (_response!=null && status >= 200 && status != 204 && status != 304) + if (response!=null && status >= 200 && status != 204 && status != 304) header.put(CONTENT_LENGTH_0); break; case EOF_CONTENT: - _persistent = _request!=null; + _persistent = request!=null; break; case CHUNKED_CONTENT: @@ -780,7 +791,7 @@ public class HttpGenerator } // If this is a response, work out persistence - if (_response!=null) + if (response!=null) { if (!isPersistent() && (close || _info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal())) { @@ -814,8 +825,8 @@ public class HttpGenerator } } - if (!has_server && status>199 && getSendServerVersion()) - header.put(SERVER); + if (status>199) + header.put(SEND[send]); // end the header. header.put(HttpTokens.CRLF); @@ -851,7 +862,12 @@ public class HttpGenerator private static final byte[] HTTP_1_1_SPACE = StringUtil.getBytes(HttpVersion.HTTP_1_1+" "); private static final byte[] CRLF = StringUtil.getBytes("\015\012"); private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\015\012"); - private static byte[] SERVER = StringUtil.getBytes("Server: Jetty(7.0.x)\015\012"); + private static final byte[][] SEND = new byte[][]{ + new byte[0], + StringUtil.getBytes("Server: Jetty(9.x.x)\015\012"), + StringUtil.getBytes("X-Powered-By: Jetty(9.x.x)\015\012"), + StringUtil.getBytes("Server: Jetty(9.x.x)\015\012X-Powered-By: Jetty(9.x.x)\015\012") + }; /* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */ diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java index 202eae1a9bf..8bba922ce80 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java @@ -108,6 +108,8 @@ public enum HttpHeader SET_COOKIE2("Set-Cookie2"), MIME_VERSION("MIME-Version"), IDENTITY("identity"), + + X_POWERED_BY("X-Powered-By"), UNKNOWN("::UNKNOWN::"); diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java index 262c0acb0e2..026fd2c3cba 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java @@ -34,6 +34,7 @@ import java.util.List; import org.eclipse.jetty.http.HttpGenerator.ResponseInfo; import org.eclipse.jetty.util.BufferUtil; +import org.hamcrest.Matchers; import org.junit.Test; public class HttpGeneratorServerTest @@ -309,6 +310,54 @@ public class HttpGeneratorServerTest } } } + + @Test + public void testSendServerXPoweredBy() throws Exception + { + ByteBuffer header = BufferUtil.allocate(8096); + ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false); + HttpFields fields = new HttpFields(); + fields.add(HttpHeader.SERVER,"SomeServer"); + fields.add(HttpHeader.X_POWERED_BY,"SomePower"); + ResponseInfo infoF = new ResponseInfo(HttpVersion.HTTP_1_1, fields, -1, 200, null, false); + String head; + + HttpGenerator gen = new HttpGenerator(true,true); + gen.generateResponse(info, header, null, null, true); + head = BufferUtil.toString(header); + BufferUtil.clear(header); + assertThat(head, containsString("HTTP/1.1 200 OK")); + assertThat(head, containsString("Server: Jetty(9.x.x)")); + assertThat(head, containsString("X-Powered-By: Jetty(9.x.x)")); + gen.reset(); + gen.generateResponse(infoF, header, null, null, true); + head = BufferUtil.toString(header); + BufferUtil.clear(header); + assertThat(head, containsString("HTTP/1.1 200 OK")); + assertThat(head, not(containsString("Server: Jetty(9.x.x)"))); + assertThat(head, containsString("Server: SomeServer")); + assertThat(head, containsString("X-Powered-By: Jetty(9.x.x)")); + assertThat(head, containsString("X-Powered-By: SomePower")); + gen.reset(); + + gen = new HttpGenerator(false,false); + gen.generateResponse(info, header, null, null, true); + head = BufferUtil.toString(header); + BufferUtil.clear(header); + assertThat(head, containsString("HTTP/1.1 200 OK")); + assertThat(head, not(containsString("Server: Jetty(9.x.x)"))); + assertThat(head, not(containsString("X-Powered-By: Jetty(9.x.x)"))); + gen.reset(); + gen.generateResponse(infoF, header, null, null, true); + head = BufferUtil.toString(header); + BufferUtil.clear(header); + assertThat(head, containsString("HTTP/1.1 200 OK")); + assertThat(head, not(containsString("Server: Jetty(9.x.x)"))); + assertThat(head, containsString("Server: SomeServer")); + assertThat(head, not(containsString("X-Powered-By: Jetty(9.x.x)"))); + assertThat(head, containsString("X-Powered-By: SomePower")); + gen.reset(); + } @Test public void testResponseNoContent() throws Exception diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java index e6ed10b185b..85f3b90ae02 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java @@ -48,6 +48,7 @@ public class HttpConfiguration private int _securePort; private String _secureScheme = HttpScheme.HTTPS.asString(); private boolean _sendServerVersion = true; //send Server: header + private boolean _sendXPoweredBy = false; //send X-Powered-By: header private boolean _sendDateHeader = false; //send Date: header @@ -150,11 +151,22 @@ public class HttpConfiguration _sendServerVersion = sendServerVersion; } - @ManagedAttribute("if true, include the server version in HTTP headers") + @ManagedAttribute("if true, send the Server header in responses") public boolean getSendServerVersion() { return _sendServerVersion; } + + public void setSendXPoweredBy (boolean sendXPoweredBy) + { + _sendXPoweredBy=sendXPoweredBy; + } + + @ManagedAttribute("if true, send the X-Powered-By header in responses") + public boolean getSendXPoweredBy() + { + return _sendXPoweredBy; + } public void setSendDateHeader(boolean sendDateHeader) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 616dc0d8e91..20b74ea5959 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -91,8 +91,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http _config = config; _connector = connector; _bufferPool = _connector.getByteBufferPool(); - _generator = new HttpGenerator(); - _generator.setSendServerVersion(_config.getSendServerVersion()); + _generator = new HttpGenerator(_config.getSendServerVersion(),_config.getSendXPoweredBy()); _channel = new HttpChannelOverHttp(connector, config, endPoint, this, new Input()); _parser = newHttpParser();