/** * The local endpoint has reached a write failure. * * <p>A low level I/O failure, or even a jetty side EndPoint close (from idle timeout) are a few * scenarios * * @param t the throwable that caused the write failure */ public void onWriteFailure(Throwable t) { ConnectionState event = null; synchronized (this) { if (this.state == ConnectionState.CLOSED) { // already closed return; } // Build out Close Reason String reason = "WebSocket Write Failure"; if (t instanceof EOFException) { reason = "WebSocket Write EOF"; Throwable cause = t.getCause(); if ((cause != null) && (StringUtil.isNotBlank(cause.getMessage()))) { reason = "EOF: " + cause.getMessage(); } } else { if (StringUtil.isNotBlank(t.getMessage())) { reason = t.getMessage(); } } CloseInfo close = new CloseInfo(StatusCode.ABNORMAL, reason); finalClose.compareAndSet(null, close); this.cleanClose = false; this.state = ConnectionState.CLOSED; this.inputAvailable = false; this.outputAvailable = false; this.closeHandshakeSource = CloseHandshakeSource.ABNORMAL; event = this.state; } notifyStateListeners(event); }
/* ------------------------------------------------------------------------------- */ public static void setJettyVersion(String serverVersion) { SEND[SEND_SERVER] = StringUtil.getBytes("Server: " + serverVersion + "\015\012"); SEND[SEND_XPOWEREDBY] = StringUtil.getBytes("X-Powered-By: " + serverVersion + "\015\012"); SEND[SEND_SERVER | SEND_XPOWEREDBY] = StringUtil.getBytes( "Server: " + serverVersion + "\015\012X-Powered-By: " + serverVersion + "\015\012"); }
/* ------------------------------------------------------------ */ private void generateRequestLine(MetaData.Request request, ByteBuffer header) { header.put(StringUtil.getBytes(request.getMethod())); header.put((byte) ' '); header.put(StringUtil.getBytes(request.getURIString())); switch (request.getVersion()) { case HTTP_1_0: case HTTP_1_1: header.put((byte) ' '); header.put(request.getVersion().toBytes()); break; default: throw new IllegalStateException(); } header.put(HttpTokens.CRLF); }
/** * Similar to the server side 6.2.3 testcase. Lots of small 1 byte UTF8 Text frames, representing * 1 overall text message. */ @Test public void testParseCase6_2_3() { String utf8 = "Hello-\uC2B5@\uC39F\uC3A4\uC3BC\uC3A0\uC3A1-UTF-8!!"; byte msg[] = StringUtil.getUtf8Bytes(utf8); List<WebSocketFrame> send = new ArrayList<>(); int len = msg.length; byte opcode = OpCode.TEXT; byte mini[]; for (int i = 0; i < len; i++) { WebSocketFrame frame = new WebSocketFrame(opcode); mini = new byte[1]; mini[0] = msg[i]; frame.setPayload(mini); boolean isLast = (i >= (len - 1)); frame.setFin(isLast); send.add(frame); opcode = OpCode.CONTINUATION; } send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); ByteBuffer completeBuf = UnitGenerator.generate(send); UnitParser parser = new UnitParser(); IncomingFramesCapture capture = new IncomingFramesCapture(); parser.setIncomingFramesHandler(capture); parser.parse(completeBuf); capture.assertErrorCount(0); capture.assertHasFrame(OpCode.TEXT, len); capture.assertHasFrame(OpCode.CLOSE, 1); }
@Override public Extension newInstance(ExtensionConfig config) { if (config == null) { return null; } String name = config.getName(); if (StringUtil.isBlank(name)) { return null; } Class<? extends Extension> extClass = getExtension(name); if (extClass == null) { return null; } try { Extension ext = extClass.newInstance(); if (ext instanceof AbstractExtension) { AbstractExtension aext = (AbstractExtension) ext; aext.setConfig(config); aext.setPolicy(policy); aext.setBufferPool(bufferPool); } return ext; } catch (InstantiationException | IllegalAccessException e) { throw new WebSocketException("Cannot instantiate extension: " + extClass, e); } }
@Override public void setCharacterEncoding(String encoding) { if (isIncluding()) return; if (_outputType == OutputType.NONE && !isCommitted()) { if (encoding == null) { // Clear any encoding. if (_characterEncoding != null) { _characterEncoding = null; if (_contentType != null) { _contentType = MimeTypes.getContentTypeWithoutCharset(_contentType); HttpField field = HttpField.CONTENT_TYPE.get(_contentType); if (field != null) _fields.put(field); else _fields.put(HttpHeader.CONTENT_TYPE, _contentType); } } } else { // No, so just add this one to the mimetype _characterEncoding = StringUtil.normalizeCharset(encoding); if (_contentType != null) { _contentType = MimeTypes.getContentTypeWithoutCharset(_contentType) + ";charset=" + _characterEncoding; HttpField field = HttpField.CONTENT_TYPE.get(_contentType); if (field != null) _fields.put(field); else _fields.put(HttpHeader.CONTENT_TYPE, _contentType); } } } }
public WebSocketSession(URI requestURI, EventDriver websocket, LogicalConnection connection) { if (requestURI == null) { throw new RuntimeException("Request URI cannot be null"); } this.requestURI = requestURI; this.websocket = websocket; this.connection = connection; this.outgoingHandler = connection; this.incomingHandler = websocket; // Get the parameter map (use the jetty MultiMap to do this right) MultiMap<String> params = new MultiMap<>(); String query = requestURI.getQuery(); if (StringUtil.isNotBlank(query)) { UrlEncoded.decodeTo(query, params, StringUtil.__UTF8); } for (String name : params.keySet()) { List<String> valueList = params.getValues(name); String valueArr[] = new String[valueList.size()]; valueArr = valueList.toArray(valueArr); parameterMap.put(name, valueArr); } }
public ByteBuffer asByteBuffer() { if ((statusCode == StatusCode.NO_CLOSE) || (statusCode == StatusCode.NO_CODE) || (statusCode == (-1))) { // codes that are not allowed to be used in endpoint. return null; } ByteBuffer buf = ByteBuffer.allocate(WebSocketFrame.MAX_CONTROL_PAYLOAD); buf.putChar((char) statusCode); if (StringUtil.isNotBlank(reason)) { byte utf[] = StringUtil.getUtf8Bytes(reason); buf.put(utf, 0, utf.length); } BufferUtil.flipToFlush(buf, 0); return buf; }
/* ------------------------------------------------------------ */ private byte[] getReasonBytes(String reason) { if (reason.length() > 1024) reason = reason.substring(0, 1024); byte[] _bytes = StringUtil.getBytes(reason); for (int i = _bytes.length; i-- > 0; ) if (_bytes[i] == '\r' || _bytes[i] == '\n') _bytes[i] = '?'; return _bytes; }
/** * Set a list of IP addresses that will not be rate limited. * * @param commaSeparatedList comma-separated whitelist */ public void setWhitelist(String commaSeparatedList) { List<String> result = new ArrayList<>(); for (String address : StringUtil.csvSplit(commaSeparatedList)) addWhitelistAddress(result, address); clearWhitelist(); _whitelist.addAll(result); LOG.debug("Whitelisted IP addresses: {}", result); }
/** Format "EEE, dd-MMM-yy HH:mm:ss 'GMT'" for cookies */ public void formatCookieDate(StringBuilder buf, long date) { gc.setTimeInMillis(date); int day_of_week = gc.get(Calendar.DAY_OF_WEEK); int day_of_month = gc.get(Calendar.DAY_OF_MONTH); int month = gc.get(Calendar.MONTH); int year = gc.get(Calendar.YEAR); year = year % 10000; int epoch = (int) ((date / 1000) % (60 * 60 * 24)); int seconds = epoch % 60; epoch = epoch / 60; int minutes = epoch % 60; int hours = epoch / 60; buf.append(DAYS[day_of_week]); buf.append(','); buf.append(' '); StringUtil.append2digits(buf, day_of_month); buf.append('-'); buf.append(MONTHS[month]); buf.append('-'); StringUtil.append2digits(buf, year / 100); StringUtil.append2digits(buf, year % 100); buf.append(' '); StringUtil.append2digits(buf, hours); buf.append(':'); StringUtil.append2digits(buf, minutes); buf.append(':'); StringUtil.append2digits(buf, seconds); buf.append(" GMT"); }
/** Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'" */ public String formatDate(long date) { buf.setLength(0); gc.setTimeInMillis(date); int day_of_week = gc.get(Calendar.DAY_OF_WEEK); int day_of_month = gc.get(Calendar.DAY_OF_MONTH); int month = gc.get(Calendar.MONTH); int year = gc.get(Calendar.YEAR); int century = year / 100; year = year % 100; int hours = gc.get(Calendar.HOUR_OF_DAY); int minutes = gc.get(Calendar.MINUTE); int seconds = gc.get(Calendar.SECOND); buf.append(DAYS[day_of_week]); buf.append(','); buf.append(' '); StringUtil.append2digits(buf, day_of_month); buf.append(' '); buf.append(MONTHS[month]); buf.append(' '); StringUtil.append2digits(buf, century); StringUtil.append2digits(buf, year); buf.append(' '); StringUtil.append2digits(buf, hours); buf.append(':'); StringUtil.append2digits(buf, minutes); buf.append(':'); StringUtil.append2digits(buf, seconds); buf.append(" GMT"); return buf.toString(); }
@Test public void testInflateBasics() throws Exception { // should result in "info:" text if properly inflated byte rawbuf[] = TypeUtil.fromHexString("CaCc4bCbB70200"); // what pywebsocket produces // byte rawbuf[] = TypeUtil.fromHexString("CbCc4bCbB70200"); // what java produces Inflater inflater = new Inflater(true); inflater.reset(); inflater.setInput(rawbuf, 0, rawbuf.length); byte outbuf[] = new byte[64]; int len = inflater.inflate(outbuf); inflater.end(); Assert.assertThat("Inflated length", len, greaterThan(4)); String actual = StringUtil.toUTF8String(outbuf, 0, len); Assert.assertThat("Inflated text", actual, is("info:")); }
@Test public void testDeflateBasics() throws Exception { // Setup deflater basics boolean nowrap = true; Deflater compressor = new Deflater(Deflater.BEST_COMPRESSION, nowrap); compressor.setStrategy(Deflater.DEFAULT_STRATEGY); // Text to compress String text = "info:"; byte uncompressed[] = StringUtil.getUtf8Bytes(text); // Prime the compressor compressor.reset(); compressor.setInput(uncompressed, 0, uncompressed.length); compressor.finish(); // Perform compression ByteBuffer outbuf = ByteBuffer.allocate(64); BufferUtil.clearToFill(outbuf); while (!compressor.finished()) { byte out[] = new byte[64]; int len = compressor.deflate(out, 0, out.length, Deflater.SYNC_FLUSH); if (len > 0) { outbuf.put(out, 0, len); } } compressor.end(); BufferUtil.flipToFlush(outbuf, 0); byte b0 = outbuf.get(0); if ((b0 & 1) != 0) { outbuf.put(0, (b0 ^= 1)); } byte compressed[] = BufferUtil.toArray(outbuf); String actual = TypeUtil.toHexString(compressed); String expected = "CaCc4bCbB70200"; // what pywebsocket produces Assert.assertThat("Compressed data", actual, is(expected)); }
public void sendStandardRequest() throws IOException { StringBuilder req = new StringBuilder(); req.append("GET /chat HTTP/1.1\r\n"); req.append("Host: ").append(destHttpURI.getHost()); if (destHttpURI.getPort() > 0) { req.append(':').append(destHttpURI.getPort()); } req.append("\r\n"); req.append("Upgrade: websocket\r\n"); req.append("Connection: Upgrade\r\n"); req.append("Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"); req.append("Sec-WebSocket-Origin: ").append(destWebsocketURI.toASCIIString()).append("\r\n"); if (StringUtil.isNotBlank(protocols)) { req.append("Sec-WebSocket-Protocol: ").append(protocols).append("\r\n"); } for (String xtension : extensions) { req.append("Sec-WebSocket-Extensions: ").append(xtension).append("\r\n"); } req.append("Sec-WebSocket-Version: ").append(version).append("\r\n"); req.append("\r\n"); writeRaw(req.toString()); }
/** * HttpGenerator. Builds HTTP Messages. * * <p>If the system property "org.eclipse.jetty.http.HttpGenerator.STRICT" is set to true, then the * generator will strictly pass on the exact strings received from methods and header fields. * Otherwise a fast case insensitive string lookup is used that may alter the case and white space * of some methods/headers */ public class HttpGenerator { private static final Logger LOG = Log.getLogger(HttpGenerator.class); public static final boolean __STRICT = Boolean.getBoolean("org.eclipse.jetty.http.HttpGenerator.STRICT"); private static final byte[] __colon_space = new byte[] {':', ' '}; private static final HttpHeaderValue[] CLOSE = {HttpHeaderValue.CLOSE}; public static final MetaData.Response CONTINUE_100_INFO = new MetaData.Response(HttpVersion.HTTP_1_1, 100, null, null, -1); public static final MetaData.Response PROGRESS_102_INFO = new MetaData.Response(HttpVersion.HTTP_1_1, 102, null, null, -1); public static final MetaData.Response RESPONSE_500_INFO = new MetaData.Response( HttpVersion.HTTP_1_1, HttpStatus.INTERNAL_SERVER_ERROR_500, null, new HttpFields() { { put(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE); } }, 0); // states public enum State { START, COMMITTED, COMPLETING, COMPLETING_1XX, END } public enum Result { NEED_CHUNK, NEED_INFO, NEED_HEADER, FLUSH, CONTINUE, SHUTDOWN_OUT, DONE } // other statics public static final int CHUNK_SIZE = 12; private State _state = State.START; private EndOfContent _endOfContent = EndOfContent.UNKNOWN_CONTENT; private long _contentPrepared = 0; private boolean _noContent = false; private Boolean _persistent = null; private final int _send; private static final int SEND_SERVER = 0x01; private static final int SEND_XPOWEREDBY = 0x02; /* ------------------------------------------------------------------------------- */ public static void setJettyVersion(String serverVersion) { SEND[SEND_SERVER] = StringUtil.getBytes("Server: " + serverVersion + "\015\012"); SEND[SEND_XPOWEREDBY] = StringUtil.getBytes("X-Powered-By: " + serverVersion + "\015\012"); SEND[SEND_SERVER | SEND_XPOWEREDBY] = StringUtil.getBytes( "Server: " + serverVersion + "\015\012X-Powered-By: " + serverVersion + "\015\012"); } /* ------------------------------------------------------------------------------- */ // data private boolean _needCRLF = false; /* ------------------------------------------------------------------------------- */ public HttpGenerator() { this(false, false); } /* ------------------------------------------------------------------------------- */ public HttpGenerator(boolean sendServerVersion, boolean sendXPoweredBy) { _send = (sendServerVersion ? SEND_SERVER : 0) | (sendXPoweredBy ? SEND_XPOWEREDBY : 0); } /* ------------------------------------------------------------------------------- */ public void reset() { _state = State.START; _endOfContent = EndOfContent.UNKNOWN_CONTENT; _noContent = false; _persistent = null; _contentPrepared = 0; _needCRLF = false; } /* ------------------------------------------------------------ */ @Deprecated public boolean getSendServerVersion() { return (_send & SEND_SERVER) != 0; } /* ------------------------------------------------------------ */ @Deprecated public void setSendServerVersion(boolean sendServerVersion) { throw new UnsupportedOperationException(); } /* ------------------------------------------------------------ */ public State getState() { return _state; } /* ------------------------------------------------------------ */ public boolean isState(State state) { return _state == state; } /* ------------------------------------------------------------ */ public boolean isIdle() { return _state == State.START; } /* ------------------------------------------------------------ */ public boolean isEnd() { return _state == State.END; } /* ------------------------------------------------------------ */ public boolean isCommitted() { return _state.ordinal() >= State.COMMITTED.ordinal(); } /* ------------------------------------------------------------ */ public boolean isChunking() { return _endOfContent == EndOfContent.CHUNKED_CONTENT; } /* ------------------------------------------------------------ */ public boolean isNoContent() { return _noContent; } /* ------------------------------------------------------------ */ public void setPersistent(boolean persistent) { _persistent = persistent; } /* ------------------------------------------------------------ */ /** @return true if known to be persistent */ public boolean isPersistent() { return Boolean.TRUE.equals(_persistent); } /* ------------------------------------------------------------ */ public boolean isWritten() { return _contentPrepared > 0; } /* ------------------------------------------------------------ */ public long getContentPrepared() { return _contentPrepared; } /* ------------------------------------------------------------ */ public void abort() { _persistent = false; _state = State.END; _endOfContent = null; } /* ------------------------------------------------------------ */ public Result generateRequest( MetaData.Request info, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException { switch (_state) { case START: { if (info == null) return Result.NEED_INFO; // Do we need a request header if (header == null) return Result.NEED_HEADER; // If we have not been told our persistence, set the default if (_persistent == null) { _persistent = info.getVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal(); if (!_persistent && HttpMethod.CONNECT.is(info.getMethod())) _persistent = true; } // prepare the header int pos = BufferUtil.flipToFill(header); try { // generate ResponseLine generateRequestLine(info, header); if (info.getVersion() == HttpVersion.HTTP_0_9) _noContent = true; else generateHeaders(info, header, content, last); boolean expect100 = info.getFields().contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()); if (expect100) { _state = State.COMMITTED; } else { // handle the content. int len = BufferUtil.length(content); if (len > 0) { _contentPrepared += len; if (isChunking()) prepareChunk(header, len); } _state = last ? State.COMPLETING : State.COMMITTED; } return Result.FLUSH; } catch (Exception e) { String message = (e instanceof BufferOverflowException) ? "Request header too large" : e.getMessage(); throw new IOException(message, e); } finally { BufferUtil.flipToFlush(header, pos); } } case COMMITTED: { int len = BufferUtil.length(content); if (len > 0) { // Do we need a chunk buffer? if (isChunking()) { // Do we need a chunk buffer? if (chunk == null) return Result.NEED_CHUNK; BufferUtil.clearToFill(chunk); prepareChunk(chunk, len); BufferUtil.flipToFlush(chunk, 0); } _contentPrepared += len; } if (last) _state = State.COMPLETING; return len > 0 ? Result.FLUSH : Result.CONTINUE; } case COMPLETING: { if (BufferUtil.hasContent(content)) { if (LOG.isDebugEnabled()) LOG.debug("discarding content in COMPLETING"); BufferUtil.clear(content); } if (isChunking()) { // Do we need a chunk buffer? if (chunk == null) return Result.NEED_CHUNK; BufferUtil.clearToFill(chunk); prepareChunk(chunk, 0); BufferUtil.flipToFlush(chunk, 0); _endOfContent = EndOfContent.UNKNOWN_CONTENT; return Result.FLUSH; } _state = State.END; return Boolean.TRUE.equals(_persistent) ? Result.DONE : Result.SHUTDOWN_OUT; } case END: if (BufferUtil.hasContent(content)) { if (LOG.isDebugEnabled()) LOG.debug("discarding content in COMPLETING"); BufferUtil.clear(content); } return Result.DONE; default: throw new IllegalStateException(); } } /* ------------------------------------------------------------ */ public Result generateResponse( MetaData.Response info, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException { return generateResponse(info, false, header, chunk, content, last); } /* ------------------------------------------------------------ */ public Result generateResponse( MetaData.Response info, boolean head, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException { switch (_state) { case START: { if (info == null) return Result.NEED_INFO; // Handle 0.9 if (info.getVersion() == HttpVersion.HTTP_0_9) { _persistent = false; _endOfContent = EndOfContent.EOF_CONTENT; if (BufferUtil.hasContent(content)) _contentPrepared += content.remaining(); _state = last ? State.COMPLETING : State.COMMITTED; return Result.FLUSH; } // Do we need a response header if (header == null) return Result.NEED_HEADER; // If we have not been told our persistence, set the default if (_persistent == null) _persistent = (info.getVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal()); // prepare the header int pos = BufferUtil.flipToFill(header); try { // generate ResponseLine generateResponseLine(info, header); // Handle 1xx and no content responses int status = info.getStatus(); if (status >= 100 && status < 200) { _noContent = true; if (status != HttpStatus.SWITCHING_PROTOCOLS_101) { header.put(HttpTokens.CRLF); _state = State.COMPLETING_1XX; return Result.FLUSH; } } else if (status == HttpStatus.NO_CONTENT_204 || status == HttpStatus.NOT_MODIFIED_304) { _noContent = true; } generateHeaders(info, header, content, last); // handle the content. int len = BufferUtil.length(content); if (len > 0) { _contentPrepared += len; if (isChunking() && !head) prepareChunk(header, len); } _state = last ? State.COMPLETING : State.COMMITTED; } catch (Exception e) { String message = (e instanceof BufferOverflowException) ? "Response header too large" : e.getMessage(); throw new IOException(message, e); } finally { BufferUtil.flipToFlush(header, pos); } return Result.FLUSH; } case COMMITTED: { int len = BufferUtil.length(content); // handle the content. if (len > 0) { if (isChunking()) { if (chunk == null) return Result.NEED_CHUNK; BufferUtil.clearToFill(chunk); prepareChunk(chunk, len); BufferUtil.flipToFlush(chunk, 0); } _contentPrepared += len; } if (last) { _state = State.COMPLETING; return len > 0 ? Result.FLUSH : Result.CONTINUE; } return len > 0 ? Result.FLUSH : Result.DONE; } case COMPLETING_1XX: { reset(); return Result.DONE; } case COMPLETING: { if (BufferUtil.hasContent(content)) { if (LOG.isDebugEnabled()) LOG.debug("discarding content in COMPLETING"); BufferUtil.clear(content); } if (isChunking()) { // Do we need a chunk buffer? if (chunk == null) return Result.NEED_CHUNK; // Write the last chunk BufferUtil.clearToFill(chunk); prepareChunk(chunk, 0); BufferUtil.flipToFlush(chunk, 0); _endOfContent = EndOfContent.UNKNOWN_CONTENT; return Result.FLUSH; } _state = State.END; return Boolean.TRUE.equals(_persistent) ? Result.DONE : Result.SHUTDOWN_OUT; } case END: if (BufferUtil.hasContent(content)) { if (LOG.isDebugEnabled()) LOG.debug("discarding content in COMPLETING"); BufferUtil.clear(content); } return Result.DONE; default: throw new IllegalStateException(); } } /* ------------------------------------------------------------ */ private void prepareChunk(ByteBuffer chunk, int remaining) { // if we need CRLF add this to header if (_needCRLF) BufferUtil.putCRLF(chunk); // Add the chunk size to the header if (remaining > 0) { BufferUtil.putHexInt(chunk, remaining); BufferUtil.putCRLF(chunk); _needCRLF = true; } else { chunk.put(LAST_CHUNK); _needCRLF = false; } } /* ------------------------------------------------------------ */ private void generateRequestLine(MetaData.Request request, ByteBuffer header) { header.put(StringUtil.getBytes(request.getMethod())); header.put((byte) ' '); header.put(StringUtil.getBytes(request.getURIString())); switch (request.getVersion()) { case HTTP_1_0: case HTTP_1_1: header.put((byte) ' '); header.put(request.getVersion().toBytes()); break; default: throw new IllegalStateException(); } header.put(HttpTokens.CRLF); } /* ------------------------------------------------------------ */ private void generateResponseLine(MetaData.Response response, ByteBuffer header) { // Look for prepared response line int status = response.getStatus(); PreparedResponse preprepared = status < __preprepared.length ? __preprepared[status] : null; String reason = response.getReason(); if (preprepared != null) { if (reason == null) header.put(preprepared._responseLine); else { header.put(preprepared._schemeCode); header.put(getReasonBytes(reason)); header.put(HttpTokens.CRLF); } } else // generate response line { header.put(HTTP_1_1_SPACE); header.put((byte) ('0' + status / 100)); header.put((byte) ('0' + (status % 100) / 10)); header.put((byte) ('0' + (status % 10))); header.put((byte) ' '); if (reason == null) { header.put((byte) ('0' + status / 100)); header.put((byte) ('0' + (status % 100) / 10)); header.put((byte) ('0' + (status % 10))); } else header.put(getReasonBytes(reason)); header.put(HttpTokens.CRLF); } } /* ------------------------------------------------------------ */ private byte[] getReasonBytes(String reason) { if (reason.length() > 1024) reason = reason.substring(0, 1024); byte[] _bytes = StringUtil.getBytes(reason); for (int i = _bytes.length; i-- > 0; ) if (_bytes[i] == '\r' || _bytes[i] == '\n') _bytes[i] = '?'; return _bytes; } /* ------------------------------------------------------------ */ private void generateHeaders( MetaData _info, ByteBuffer header, ByteBuffer content, boolean last) { final MetaData.Request request = (_info instanceof MetaData.Request) ? (MetaData.Request) _info : null; final MetaData.Response response = (_info instanceof MetaData.Response) ? (MetaData.Response) _info : null; // default field values int send = _send; HttpField transfer_encoding = null; boolean keep_alive = false; boolean close = false; boolean content_type = false; StringBuilder connection = null; // Generate fields if (_info.getFields() != null) { for (HttpField field : _info.getFields()) { String v = field.getValue(); if (v == null || v.length() == 0) continue; // rfc7230 does not allow no value HttpHeader h = field.getHeader(); switch (h == null ? HttpHeader.UNKNOWN : h) { case CONTENT_LENGTH: // handle specially below if (_info.getContentLength() >= 0) _endOfContent = EndOfContent.CONTENT_LENGTH; break; case CONTENT_TYPE: { if (field.getValue().startsWith(MimeTypes.Type.MULTIPART_BYTERANGES.toString())) _endOfContent = EndOfContent.SELF_DEFINING_CONTENT; // write the field to the header content_type = true; putTo(field, header); break; } case TRANSFER_ENCODING: { if (_info.getVersion() == HttpVersion.HTTP_1_1) transfer_encoding = field; // Do NOT add yet! break; } case CONNECTION: { if (request != null) putTo(field, header); // Lookup and/or split connection value field HttpHeaderValue[] values = HttpHeaderValue.CLOSE.is(field.getValue()) ? CLOSE : new HttpHeaderValue[] {HttpHeaderValue.CACHE.get(field.getValue())}; String[] split = null; if (values[0] == null) { split = StringUtil.csvSplit(field.getValue()); if (split.length > 0) { values = new HttpHeaderValue[split.length]; for (int i = 0; i < split.length; i++) values[i] = HttpHeaderValue.CACHE.get(split[i]); } } // Handle connection values for (int i = 0; i < values.length; i++) { HttpHeaderValue value = values[i]; switch (value == null ? HttpHeaderValue.UNKNOWN : value) { case UPGRADE: { // special case for websocket connection ordering header .put(HttpHeader.CONNECTION.getBytesColonSpace()) .put(HttpHeader.UPGRADE.getBytes()); header.put(CRLF); break; } case CLOSE: { close = true; _persistent = false; if (response != null) { if (_endOfContent == EndOfContent.UNKNOWN_CONTENT) _endOfContent = EndOfContent.EOF_CONTENT; } break; } case KEEP_ALIVE: { if (_info.getVersion() == HttpVersion.HTTP_1_0) { keep_alive = true; if (response != null) _persistent = true; } break; } default: { if (connection == null) connection = new StringBuilder(); else connection.append(','); connection.append(split == null ? field.getValue() : split[i]); } } } // Do NOT add yet! break; } case SERVER: { send = send & ~SEND_SERVER; putTo(field, header); break; } default: putTo(field, header); } } } // Calculate how to end _content and connection, _content length and transfer encoding // settings. // From RFC 2616 4.4: // 1. No body for 1xx, 204, 304 & HEAD response // 2. Force _content-length? // 3. If Transfer-Encoding!=identity && HTTP/1.1 && !HttpConnection==close then chunk // 4. Content-Length // 5. multipart/byteranges // 6. close int status = response != null ? response.getStatus() : -1; switch (_endOfContent) { case UNKNOWN_CONTENT: // It may be that we have no _content, or perhaps _content just has not been // written yet? // Response known not to have a body 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) { // known length but not actually set. header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace()); BufferUtil.putDecLong(header, content_length); header.put(HttpTokens.CRLF); } } else if (last) { // we have seen all the _content there is, so we can be content-length limited. _endOfContent = EndOfContent.CONTENT_LENGTH; 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) { header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace()); BufferUtil.putDecLong(header, content_length); header.put(HttpTokens.CRLF); } } else { // No idea, so we must assume that a body is coming. _endOfContent = EndOfContent.CHUNKED_CONTENT; // HTTP 1.0 does not understand chunked content, so we must use EOF content. // For a request with HTTP 1.0 & Connection: keep-alive // we *must* close the connection, otherwise the client // has no way to detect the end of the content. if (!isPersistent() || _info.getVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal()) _endOfContent = EndOfContent.EOF_CONTENT; } break; case CONTENT_LENGTH: { long content_length = _info.getContentLength(); if ((response != null || content_length > 0 || content_type) && !_noContent) { header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace()); BufferUtil.putDecLong(header, content_length); header.put(HttpTokens.CRLF); } break; } case SELF_DEFINING_CONTENT: { // TODO - Should we do this? Why was it not required before? long content_length = _info.getContentLength(); if (content_length > 0) { header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace()); BufferUtil.putDecLong(header, content_length); header.put(HttpTokens.CRLF); } break; } case NO_CONTENT: if (response != null && status >= 200 && status != 204 && status != 304) header.put(CONTENT_LENGTH_0); break; case EOF_CONTENT: _persistent = request != null; break; case CHUNKED_CONTENT: break; default: break; } // Add transfer_encoding if needed if (isChunking()) { // try to use user supplied encoding as it may have other values. if (transfer_encoding != null && !HttpHeaderValue.CHUNKED.toString().equalsIgnoreCase(transfer_encoding.getValue())) { String c = transfer_encoding.getValue(); if (c.endsWith(HttpHeaderValue.CHUNKED.toString())) putTo(transfer_encoding, header); else throw new IllegalArgumentException("BAD TE"); } else header.put(TRANSFER_ENCODING_CHUNKED); } // Handle connection if need be if (_endOfContent == EndOfContent.EOF_CONTENT) { keep_alive = false; _persistent = false; } // If this is a response, work out persistence if (response != null) { if (!isPersistent() && (close || _info.getVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal())) { if (connection == null) header.put(CONNECTION_CLOSE); else { header.put(CONNECTION_CLOSE, 0, CONNECTION_CLOSE.length - 2); header.put((byte) ','); header.put(StringUtil.getBytes(connection.toString())); header.put(CRLF); } } else if (keep_alive) { if (connection == null) header.put(CONNECTION_KEEP_ALIVE); else { header.put(CONNECTION_KEEP_ALIVE, 0, CONNECTION_KEEP_ALIVE.length - 2); header.put((byte) ','); header.put(StringUtil.getBytes(connection.toString())); header.put(CRLF); } } else if (connection != null) { header.put(HttpHeader.CONNECTION.getBytesColonSpace()); header.put(StringUtil.getBytes(connection.toString())); header.put(CRLF); } } if (status > 199) header.put(SEND[send]); // end the header. header.put(HttpTokens.CRLF); } /* ------------------------------------------------------------------------------- */ public static byte[] getReasonBuffer(int code) { PreparedResponse status = code < __preprepared.length ? __preprepared[code] : null; if (status != null) return status._reason; return null; } /* ------------------------------------------------------------------------------- */ @Override public String toString() { return String.format("%s@%x{s=%s}", getClass().getSimpleName(), hashCode(), _state); } /* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */ // common _content private static final byte[] LAST_CHUNK = { (byte) '0', (byte) '\015', (byte) '\012', (byte) '\015', (byte) '\012' }; private static final byte[] CONTENT_LENGTH_0 = StringUtil.getBytes("Content-Length: 0\015\012"); private static final byte[] CONNECTION_KEEP_ALIVE = StringUtil.getBytes("Connection: keep-alive\015\012"); private static final byte[] CONNECTION_CLOSE = StringUtil.getBytes("Connection: close\015\012"); 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 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") }; /* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */ // Build cache of response lines for status private static class PreparedResponse { byte[] _reason; byte[] _schemeCode; byte[] _responseLine; } private static final PreparedResponse[] __preprepared = new PreparedResponse[HttpStatus.MAX_CODE + 1]; static { int versionLength = HttpVersion.HTTP_1_1.toString().length(); for (int i = 0; i < __preprepared.length; i++) { HttpStatus.Code code = HttpStatus.getCode(i); if (code == null) continue; String reason = code.getMessage(); byte[] line = new byte[versionLength + 5 + reason.length() + 2]; HttpVersion.HTTP_1_1.toBuffer().get(line, 0, versionLength); line[versionLength + 0] = ' '; line[versionLength + 1] = (byte) ('0' + i / 100); line[versionLength + 2] = (byte) ('0' + (i % 100) / 10); line[versionLength + 3] = (byte) ('0' + (i % 10)); line[versionLength + 4] = ' '; for (int j = 0; j < reason.length(); j++) line[versionLength + 5 + j] = (byte) reason.charAt(j); line[versionLength + 5 + reason.length()] = HttpTokens.CARRIAGE_RETURN; line[versionLength + 6 + reason.length()] = HttpTokens.LINE_FEED; __preprepared[i] = new PreparedResponse(); __preprepared[i]._schemeCode = Arrays.copyOfRange(line, 0, versionLength + 5); __preprepared[i]._reason = Arrays.copyOfRange(line, versionLength + 5, line.length - 2); __preprepared[i]._responseLine = line; } } private static void putSanitisedName(String s, ByteBuffer buffer) { int l = s.length(); for (int i = 0; i < l; i++) { char c = s.charAt(i); if (c < 0 || c > 0xff || c == '\r' || c == '\n' || c == ':') buffer.put((byte) '?'); else buffer.put((byte) (0xff & c)); } } private static void putSanitisedValue(String s, ByteBuffer buffer) { int l = s.length(); for (int i = 0; i < l; i++) { char c = s.charAt(i); if (c < 0 || c > 0xff || c == '\r' || c == '\n') buffer.put((byte) ' '); else buffer.put((byte) (0xff & c)); } } public static void putTo(HttpField field, ByteBuffer bufferInFillMode) { if (field instanceof PreEncodedHttpField) { ((PreEncodedHttpField) field).putTo(bufferInFillMode, HttpVersion.HTTP_1_0); } else { HttpHeader header = field.getHeader(); if (header != null) { bufferInFillMode.put(header.getBytesColonSpace()); putSanitisedValue(field.getValue(), bufferInFillMode); } else { putSanitisedName(field.getName(), bufferInFillMode); bufferInFillMode.put(__colon_space); putSanitisedValue(field.getValue(), bufferInFillMode); } BufferUtil.putCRLF(bufferInFillMode); } } public static void putTo(HttpFields fields, ByteBuffer bufferInFillMode) { for (HttpField field : fields) { if (field != null) putTo(field, bufferInFillMode); } BufferUtil.putCRLF(bufferInFillMode); } }
private static String deTag(String raw) { return StringUtil.replace(StringUtil.replace(raw, "<", "<"), ">", ">"); }
private boolean handleKnownHeaders(ByteBuffer buffer) { boolean add_to_connection_trie = false; switch (_header) { case CONTENT_LENGTH: if (_endOfContent != EndOfContent.CHUNKED_CONTENT) { try { _contentLength = Long.parseLong(_valueString); } catch (NumberFormatException e) { LOG.ignore(e); throw new BadMessage(HttpStatus.BAD_REQUEST_400, "Bad Content-Length"); } if (_contentLength <= 0) _endOfContent = EndOfContent.NO_CONTENT; else _endOfContent = EndOfContent.CONTENT_LENGTH; } break; case TRANSFER_ENCODING: if (_value == HttpHeaderValue.CHUNKED) _endOfContent = EndOfContent.CHUNKED_CONTENT; else { if (_valueString.endsWith(HttpHeaderValue.CHUNKED.toString())) _endOfContent = EndOfContent.CHUNKED_CONTENT; else if (_valueString.indexOf(HttpHeaderValue.CHUNKED.toString()) >= 0) { throw new BadMessage(HttpStatus.BAD_REQUEST_400, "Bad chunking"); } } break; case HOST: add_to_connection_trie = _connectionFields != null && _field == null; _host = true; String host = _valueString; int port = 0; if (host == null || host.length() == 0) { throw new BadMessage(HttpStatus.BAD_REQUEST_400, "Bad Host header"); } int len = host.length(); loop: for (int i = len; i-- > 0; ) { char c2 = (char) (0xff & host.charAt(i)); switch (c2) { case ']': break loop; case ':': try { len = i; port = StringUtil.toInt(host.substring(i + 1)); } catch (NumberFormatException e) { if (DEBUG) LOG.debug(e); throw new BadMessage(HttpStatus.BAD_REQUEST_400, "Bad Host header"); } break loop; } } if (host.charAt(0) == '[') { if (host.charAt(len - 1) != ']') { throw new BadMessage(HttpStatus.BAD_REQUEST_400, "Bad IPv6 Host header"); } host = host.substring(1, len - 1); } else if (len != host.length()) host = host.substring(0, len); if (_requestHandler != null) _requestHandler.parsedHostHeader(host, port); break; case CONNECTION: // Don't cache if not persistent if (_valueString != null && _valueString.indexOf("close") >= 0) { _closed = true; _connectionFields = null; } break; case AUTHORIZATION: case ACCEPT: case ACCEPT_CHARSET: case ACCEPT_ENCODING: case ACCEPT_LANGUAGE: case COOKIE: case CACHE_CONTROL: case USER_AGENT: add_to_connection_trie = _connectionFields != null && _field == null; break; default: break; } if (add_to_connection_trie && !_connectionFields.isFull() && _header != null && _valueString != null) { _field = new HttpField(_header, _valueString); _connectionFields.put(_field); } return false; }
protected void commit(ByteBuffer content, boolean complete, Callback callback) { // Are we excluding because of status? Response response = _channel.getResponse(); int sc = response.getStatus(); if (sc > 0 && (sc < 200 || sc == 204 || sc == 205 || sc >= 300)) { LOG.debug("{} exclude by status {}", this, sc); noCompression(); if (sc == 304) { String request_etags = (String) _channel.getRequest().getAttribute("o.e.j.s.h.gzip.GzipHandler.etag"); String response_etag = response.getHttpFields().get(HttpHeader.ETAG); if (request_etags != null && response_etag != null) { String response_etag_gzip = etagGzip(response_etag); if (request_etags.contains(response_etag_gzip)) response.getHttpFields().put(HttpHeader.ETAG, response_etag_gzip); } } _interceptor.write(content, complete, callback); return; } // Are we excluding because of mime-type? String ct = response.getContentType(); if (ct != null) { ct = MimeTypes.getContentTypeWithoutCharset(ct); if (!_factory.isMimeTypeGzipable(StringUtil.asciiToLowerCase(ct))) { LOG.debug("{} exclude by mimeType {}", this, ct); noCompression(); _interceptor.write(content, complete, callback); return; } } // Has the Content-Encoding header already been set? HttpFields fields = response.getHttpFields(); String ce = fields.get(HttpHeader.CONTENT_ENCODING); if (ce != null) { LOG.debug("{} exclude by content-encoding {}", this, ce); noCompression(); _interceptor.write(content, complete, callback); return; } // Are we the thread that commits? if (_state.compareAndSet(GZState.MIGHT_COMPRESS, GZState.COMMITTING)) { // We are varying the response due to accept encoding header. if (_vary != null) { if (fields.contains(HttpHeader.VARY)) fields.addCSV(HttpHeader.VARY, _vary.getValues()); else fields.add(_vary); } long content_length = response.getContentLength(); if (content_length < 0 && complete) content_length = content.remaining(); _deflater = _factory.getDeflater(_channel.getRequest(), content_length); if (_deflater == null) { LOG.debug("{} exclude no deflater", this); _state.set(GZState.NOT_COMPRESSING); _interceptor.write(content, complete, callback); return; } fields.put(GZIP._contentEncoding); _crc.reset(); _buffer = _channel.getByteBufferPool().acquire(_bufferSize, false); BufferUtil.fill(_buffer, GZIP_HEADER, 0, GZIP_HEADER.length); // Adjust headers response.setContentLength(-1); String etag = fields.get(HttpHeader.ETAG); if (etag != null) fields.put(HttpHeader.ETAG, etagGzip(etag)); LOG.debug("{} compressing {}", this, _deflater); _state.set(GZState.COMPRESSING); gzip(content, complete, callback); } else callback.failed(new WritePendingException()); }
/* ------------------------------------------------------------ */ private void generateHeaders( MetaData _info, ByteBuffer header, ByteBuffer content, boolean last) { final MetaData.Request request = (_info instanceof MetaData.Request) ? (MetaData.Request) _info : null; final MetaData.Response response = (_info instanceof MetaData.Response) ? (MetaData.Response) _info : null; // default field values int send = _send; HttpField transfer_encoding = null; boolean keep_alive = false; boolean close = false; boolean content_type = false; StringBuilder connection = null; // Generate fields if (_info.getFields() != null) { for (HttpField field : _info.getFields()) { String v = field.getValue(); if (v == null || v.length() == 0) continue; // rfc7230 does not allow no value HttpHeader h = field.getHeader(); switch (h == null ? HttpHeader.UNKNOWN : h) { case CONTENT_LENGTH: // handle specially below if (_info.getContentLength() >= 0) _endOfContent = EndOfContent.CONTENT_LENGTH; break; case CONTENT_TYPE: { if (field.getValue().startsWith(MimeTypes.Type.MULTIPART_BYTERANGES.toString())) _endOfContent = EndOfContent.SELF_DEFINING_CONTENT; // write the field to the header content_type = true; putTo(field, header); break; } case TRANSFER_ENCODING: { if (_info.getVersion() == HttpVersion.HTTP_1_1) transfer_encoding = field; // Do NOT add yet! break; } case CONNECTION: { if (request != null) putTo(field, header); // Lookup and/or split connection value field HttpHeaderValue[] values = HttpHeaderValue.CLOSE.is(field.getValue()) ? CLOSE : new HttpHeaderValue[] {HttpHeaderValue.CACHE.get(field.getValue())}; String[] split = null; if (values[0] == null) { split = StringUtil.csvSplit(field.getValue()); if (split.length > 0) { values = new HttpHeaderValue[split.length]; for (int i = 0; i < split.length; i++) values[i] = HttpHeaderValue.CACHE.get(split[i]); } } // Handle connection values for (int i = 0; i < values.length; i++) { HttpHeaderValue value = values[i]; switch (value == null ? HttpHeaderValue.UNKNOWN : value) { case UPGRADE: { // special case for websocket connection ordering header .put(HttpHeader.CONNECTION.getBytesColonSpace()) .put(HttpHeader.UPGRADE.getBytes()); header.put(CRLF); break; } case CLOSE: { close = true; _persistent = false; if (response != null) { if (_endOfContent == EndOfContent.UNKNOWN_CONTENT) _endOfContent = EndOfContent.EOF_CONTENT; } break; } case KEEP_ALIVE: { if (_info.getVersion() == HttpVersion.HTTP_1_0) { keep_alive = true; if (response != null) _persistent = true; } break; } default: { if (connection == null) connection = new StringBuilder(); else connection.append(','); connection.append(split == null ? field.getValue() : split[i]); } } } // Do NOT add yet! break; } case SERVER: { send = send & ~SEND_SERVER; putTo(field, header); break; } default: putTo(field, header); } } } // Calculate how to end _content and connection, _content length and transfer encoding // settings. // From RFC 2616 4.4: // 1. No body for 1xx, 204, 304 & HEAD response // 2. Force _content-length? // 3. If Transfer-Encoding!=identity && HTTP/1.1 && !HttpConnection==close then chunk // 4. Content-Length // 5. multipart/byteranges // 6. close int status = response != null ? response.getStatus() : -1; switch (_endOfContent) { case UNKNOWN_CONTENT: // It may be that we have no _content, or perhaps _content just has not been // written yet? // Response known not to have a body 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) { // known length but not actually set. header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace()); BufferUtil.putDecLong(header, content_length); header.put(HttpTokens.CRLF); } } else if (last) { // we have seen all the _content there is, so we can be content-length limited. _endOfContent = EndOfContent.CONTENT_LENGTH; 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) { header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace()); BufferUtil.putDecLong(header, content_length); header.put(HttpTokens.CRLF); } } else { // No idea, so we must assume that a body is coming. _endOfContent = EndOfContent.CHUNKED_CONTENT; // HTTP 1.0 does not understand chunked content, so we must use EOF content. // For a request with HTTP 1.0 & Connection: keep-alive // we *must* close the connection, otherwise the client // has no way to detect the end of the content. if (!isPersistent() || _info.getVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal()) _endOfContent = EndOfContent.EOF_CONTENT; } break; case CONTENT_LENGTH: { long content_length = _info.getContentLength(); if ((response != null || content_length > 0 || content_type) && !_noContent) { header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace()); BufferUtil.putDecLong(header, content_length); header.put(HttpTokens.CRLF); } break; } case SELF_DEFINING_CONTENT: { // TODO - Should we do this? Why was it not required before? long content_length = _info.getContentLength(); if (content_length > 0) { header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace()); BufferUtil.putDecLong(header, content_length); header.put(HttpTokens.CRLF); } break; } case NO_CONTENT: if (response != null && status >= 200 && status != 204 && status != 304) header.put(CONTENT_LENGTH_0); break; case EOF_CONTENT: _persistent = request != null; break; case CHUNKED_CONTENT: break; default: break; } // Add transfer_encoding if needed if (isChunking()) { // try to use user supplied encoding as it may have other values. if (transfer_encoding != null && !HttpHeaderValue.CHUNKED.toString().equalsIgnoreCase(transfer_encoding.getValue())) { String c = transfer_encoding.getValue(); if (c.endsWith(HttpHeaderValue.CHUNKED.toString())) putTo(transfer_encoding, header); else throw new IllegalArgumentException("BAD TE"); } else header.put(TRANSFER_ENCODING_CHUNKED); } // Handle connection if need be if (_endOfContent == EndOfContent.EOF_CONTENT) { keep_alive = false; _persistent = false; } // If this is a response, work out persistence if (response != null) { if (!isPersistent() && (close || _info.getVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal())) { if (connection == null) header.put(CONNECTION_CLOSE); else { header.put(CONNECTION_CLOSE, 0, CONNECTION_CLOSE.length - 2); header.put((byte) ','); header.put(StringUtil.getBytes(connection.toString())); header.put(CRLF); } } else if (keep_alive) { if (connection == null) header.put(CONNECTION_KEEP_ALIVE); else { header.put(CONNECTION_KEEP_ALIVE, 0, CONNECTION_KEEP_ALIVE.length - 2); header.put((byte) ','); header.put(StringUtil.getBytes(connection.toString())); header.put(CRLF); } } else if (connection != null) { header.put(HttpHeader.CONNECTION.getBytesColonSpace()); header.put(StringUtil.getBytes(connection.toString())); header.put(CRLF); } } if (status > 199) header.put(SEND[send]); // end the header. header.put(HttpTokens.CRLF); }
@Override public void sendError(int code, String message) throws IOException { if (isIncluding()) return; if (isCommitted()) LOG.warn("Committed before " + code + " " + message); resetBuffer(); _characterEncoding = null; setHeader(HttpHeader.EXPIRES, null); setHeader(HttpHeader.LAST_MODIFIED, null); setHeader(HttpHeader.CACHE_CONTROL, null); setHeader(HttpHeader.CONTENT_TYPE, null); setHeader(HttpHeader.CONTENT_LENGTH, null); _outputType = OutputType.NONE; setStatus(code); _reason = message; Request request = _channel.getRequest(); Throwable cause = (Throwable) request.getAttribute(Dispatcher.ERROR_EXCEPTION); if (message == null) message = cause == null ? HttpStatus.getMessage(code) : cause.toString(); // If we are allowed to have a body if (code != SC_NO_CONTENT && code != SC_NOT_MODIFIED && code != SC_PARTIAL_CONTENT && code >= SC_OK) { ErrorHandler error_handler = null; ContextHandler.Context context = request.getContext(); if (context != null) error_handler = context.getContextHandler().getErrorHandler(); if (error_handler == null) error_handler = _channel.getServer().getBean(ErrorHandler.class); if (error_handler != null) { request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, new Integer(code)); request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message); request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, request.getRequestURI()); request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME, request.getServletName()); error_handler.handle(null, _channel.getRequest(), _channel.getRequest(), this); } else { setHeader(HttpHeader.CACHE_CONTROL, "must-revalidate,no-cache,no-store"); setContentType(MimeTypes.Type.TEXT_HTML_8859_1.toString()); ByteArrayISO8859Writer writer = new ByteArrayISO8859Writer(2048); if (message != null) { message = StringUtil.replace(message, "&", "&"); message = StringUtil.replace(message, "<", "<"); message = StringUtil.replace(message, ">", ">"); } String uri = request.getRequestURI(); if (uri != null) { uri = StringUtil.replace(uri, "&", "&"); uri = StringUtil.replace(uri, "<", "<"); uri = StringUtil.replace(uri, ">", ">"); } writer.write( "<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html;charset=ISO-8859-1\"/>\n"); writer.write("<title>Error "); writer.write(Integer.toString(code)); writer.write(' '); if (message == null) writer.write(message); writer.write("</title>\n</head>\n<body>\n<h2>HTTP ERROR: "); writer.write(Integer.toString(code)); writer.write("</h2>\n<p>Problem accessing "); writer.write(uri); writer.write(". Reason:\n<pre> "); writer.write(message); writer.write("</pre>"); writer.write("</p>\n<hr /><i><small>Powered by Jetty://</small></i>"); writer.write("\n</body>\n</html>\n"); writer.flush(); setContentLength(writer.size()); writer.writeTo(getOutputStream()); writer.destroy(); } } else if (code != SC_PARTIAL_CONTENT) { // TODO work out why this is required? _channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_TYPE); _channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_LENGTH); _characterEncoding = null; _mimeType = null; } complete(); }
public void writeRaw(String str) throws IOException { LOG.debug("write((String)[{}]){}{})", str.length(), '\n', str); out.write(StringUtil.getBytes(str, StringUtil.__ISO_8859_1)); }
protected String processFlowFile( final ComponentLog logger, final DebugLevels logLevel, final FlowFile flowFile, final ProcessSession session, final ProcessContext context) { final Set<String> attributeKeys = getAttributesToLog(flowFile.getAttributes().keySet(), context); final ComponentLog LOG = getLogger(); final String dashedLine; String logPrefix = context.getProperty(LOG_PREFIX).evaluateAttributeExpressions(flowFile).getValue(); if (StringUtil.isBlank(logPrefix)) { dashedLine = StringUtils.repeat('-', 50); } else { // abbreviate long lines logPrefix = StringUtils.abbreviate(logPrefix, 40); // center the logPrefix and pad with dashes logPrefix = StringUtils.center(logPrefix, 40, '-'); // five dashes on the left and right side, plus the dashed logPrefix dashedLine = StringUtils.repeat('-', 5) + logPrefix + StringUtils.repeat('-', 5); } // Pretty print metadata final StringBuilder message = new StringBuilder(); message.append("logging for flow file ").append(flowFile); message.append("\n"); message.append(dashedLine); message.append("\nStandard FlowFile Attributes"); message.append( String.format( "\nKey: '%1$s'\n\tValue: '%2$s'", "entryDate", new Date(flowFile.getEntryDate()))); message.append( String.format( "\nKey: '%1$s'\n\tValue: '%2$s'", "lineageStartDate", new Date(flowFile.getLineageStartDate()))); message.append(String.format("\nKey: '%1$s'\n\tValue: '%2$s'", "fileSize", flowFile.getSize())); message.append("\nFlowFile Attribute Map Content"); for (final String key : attributeKeys) { message.append( String.format("\nKey: '%1$s'\n\tValue: '%2$s'", key, flowFile.getAttribute(key))); } message.append("\n"); message.append(dashedLine); // The user can request to log the payload final boolean logPayload = context.getProperty(LOG_PAYLOAD).asBoolean(); if (logPayload) { message.append("\n"); if (flowFile.getSize() < ONE_MB) { final FlowFilePayloadCallback callback = new FlowFilePayloadCallback(); session.read(flowFile, callback); message.append(callback.getContents()); } else { message.append("\n Not including payload since it is larger than one mb."); } } final String outputMessage = message.toString().trim(); // Uses optional property to specify logging level switch (logLevel) { case info: LOG.info(outputMessage); break; case debug: LOG.debug(outputMessage); break; case warn: LOG.warn(outputMessage); break; case trace: LOG.trace(outputMessage); break; case error: LOG.error(outputMessage); break; default: LOG.debug(outputMessage); } return outputMessage; }