
com.caucho.v5.http.protocol.RequestHttp Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of baratine Show documentation
Show all versions of baratine Show documentation
A reactive Java web server.
/*
* Copyright (c) 1998-2015 Caucho Technology -- all rights reserved
*
* This file is part of Baratine(TM)
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Baratine is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Baratine is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Baratine; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.v5.http.protocol;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.caucho.v5.health.meter.MeterActiveTime;
import com.caucho.v5.health.meter.MeterAverage;
import com.caucho.v5.health.meter.MeterService;
import com.caucho.v5.http.container.HttpContainer;
import com.caucho.v5.http.dispatch.Invocation;
import com.caucho.v5.http.protocol2.Http2Constants;
import com.caucho.v5.http.protocol2.RequestProtocolHttp2;
import com.caucho.v5.io.ClientDisconnectException;
import com.caucho.v5.io.ReadBuffer;
import com.caucho.v5.io.SocketBar;
import com.caucho.v5.io.TempBuffer;
import com.caucho.v5.io.WriteBuffer;
import com.caucho.v5.network.port.ConnectionProtocol;
import com.caucho.v5.network.port.ConnectionTcp;
import com.caucho.v5.util.CharBuffer;
import com.caucho.v5.util.CharSegment;
import com.caucho.v5.util.CurrentTime;
import com.caucho.v5.util.L10N;
import com.caucho.v5.web.CookieWeb;
/**
* Parses and holds request information for an HTTP request.
*/
public class RequestHttp extends RequestHttpBase
{
private static final L10N L = new L10N(RequestHttp.class);
private static final Logger log
= Logger.getLogger(RequestHttp.class.getName());
public static final int HTTP_0_9 = 0x0009;
public static final int HTTP_1_0 = 0x0100;
public static final int HTTP_1_1 = 0x0101;
private static final CharBuffer _getCb = new CharBuffer("GET");
private static final CharBuffer _headCb = new CharBuffer("HEAD");
private static final CharBuffer _postCb = new CharBuffer("POST");
private static final byte []_http10ok = "HTTP/1.0 200 OK".getBytes();
private static final byte []_http11ok = "HTTP/1.1 200 OK".getBytes();
private static final byte []_contentLengthBytes = "\r\nContent-Length: ".getBytes();
private static final byte []_contentTypeBytes = "\r\nContent-Type: ".getBytes();
private static final byte []_textHtmlBytes = "\r\nContent-Type: text/html".getBytes();
private static final byte []_charsetBytes = "; charset=".getBytes();
private static final byte []_textHtmlLatin1Bytes = "\r\nContent-Type: text/html; charset=iso-8859-1".getBytes();
private static final byte []_connectionCloseBytes = "\r\nConnection: close".getBytes();
private static final char []_connectionCb = "Connection".toCharArray();
private static final CharBuffer _closeCb = new CharBuffer("Close");
private final byte []_serverHeaderBytes;
private static final char []_toLowerAscii;
private static final char []_toUpperAscii;
private static final boolean []_isHttpWhitespace;
private static final String METER_REQUEST_TIME
= "Caucho|Http|Request";
private static final String METER_REQUEST_READ_BYTES
= "Caucho|Http|Request Read Bytes";
private static final String METER_REQUEST_WRITE_BYTES
= "Caucho|Http|Request Write Bytes";
// private RequestProtocol _subrequest;
private final CharBuffer _method // "GET"
= new CharBuffer();
private String _methodString;
private final CharBuffer _uriHost // www.caucho.com:8080
= new CharBuffer();
private CharSequence _host;
private byte []_uri; // "/path/test.jsp/Junk?query=7"
private int _uriLength;
private final CharBuffer _protocol // "HTTP/1.0"
= new CharBuffer();
private int _version;
private char []_headerBuffer;
private int _headerLength;
private CharSegment []_headerKeys;
private CharSegment []_headerValues;
private int _headerSize;
private InChunked _chunkedInputStream = new InChunked();
private InContentLength _contentLengthStream = new InContentLength();
// private InRaw _rawInputStream = new InRaw();
private boolean _isChunkedIn;
private long _inOffset;
private KeepaliveState _keepalive = KeepaliveState.INIT;
private MeterActiveTime _meterRequestTime;
private MeterAverage _meterRequestReadBytes;
private MeterAverage _meterRequestWriteBytes;
/*
private final CharBuffer _cb = new CharBuffer();
private final StringBuilder _sb = new StringBuilder();
*/
/*
private final byte []_dateBuffer = new byte[256];
private final CharBuffer _dateCharBuffer = new CharBuffer();
private int _dateBufferLength;
private long _lastDate;
*/
//private WriteStream _rawWrite;
//private OutHttpProxy _outProxy;
private boolean _isUpgrade;
private RequestFacade _request;
private PendingFirst _pending;
private final RequestHttpState _state;
// private HmuxRequest _hmuxRequest;
/**
* Creates a new HttpRequest. New connections reuse the request.
*
* @param server the owning server.
*/
public RequestHttp(ProtocolHttp protocolHttp,
RequestHttpState state)
{
super(protocolHttp);
Objects.requireNonNull(state);
_state = state;
_meterRequestTime
= MeterService.createActiveTimeMeter(METER_REQUEST_TIME);
_meterRequestReadBytes
= MeterService.createAverageMeter(METER_REQUEST_READ_BYTES, "");
_meterRequestWriteBytes
= MeterService.createAverageMeter(METER_REQUEST_WRITE_BYTES, "");
String serverHeader = protocolHttp().serverHeader();
_serverHeaderBytes = ("\r\nServer: " + serverHeader).getBytes();
}
public void init(RequestFacade request)
{
_request = request;
}
/**
* Returns true for the top-level request, but false for any include()
* or forward()
*/
public boolean isTop()
{
return true;
}
protected boolean checkLogin()
{
return true;
}
//
// HTTP request properties
//
/**
* Returns a buffer containing the request method.
*/
public CharSegment getMethodBuffer()
{
return _method;
}
/**
* Returns the HTTP method (GET, POST, HEAD, etc.)
*/
@Override
public String getMethod()
{
if (_methodString == null) {
CharSegment cb = getMethodBuffer();
if (cb.length() == 0) {
_methodString = "GET";
return _methodString;
}
switch (cb.charAt(0)) {
case 'G':
_methodString = cb.equals(_getCb) ? "GET" : cb.toString();
break;
case 'H':
_methodString = cb.equals(_headCb) ? "HEAD" : cb.toString();
break;
case 'P':
_methodString = cb.equals(_postCb) ? "POST" : cb.toString();
break;
default:
_methodString = cb.toString();
}
}
return _methodString;
}
/**
* Returns the virtual host of the request
*/
@Override
protected CharSequence getHost()
{
if (_host != null)
return _host;
String virtualHost = getConnection().getVirtualHost();
if (virtualHost != null) {
_host = virtualHost;
}
else if (_uriHost.length() > 0) {
_host = _uriHost;
}
else if ((_host = getForwardedHostHeader()) != null) {
}
else {
_host = getHostHeader();
}
return _host;
}
/**
* Returns the virtual host from the invocation
*/
private CharSequence getInvocationHost()
throws IOException
{
if (_host != null) {
}
else if (_uriHost.length() > 0) {
_host = _uriHost;
}
else if ((_host = getForwardedHostHeader()) != null) {
}
else if ((_host = getHostHeader()) != null) {
}
else if (HTTP_1_1 <= getVersion()) {
throw new BadRequestException(L.l("HTTP/1.1 requires a Host header (Remote IP={0})", remoteHost()));
}
return _host;
}
/**
* Returns the byte buffer containing the request URI
*/
@Override
public byte []getUriBuffer()
{
return _uri;
}
/**
* Returns the length of the request URI
*/
@Override
public int getUriLength()
{
return _uriLength;
}
/**
* Returns the protocol.
*/
@Override
public String getProtocol()
{
switch (_version) {
case HTTP_1_1:
return "HTTP/1.1";
case HTTP_1_0:
return "HTTP/1.0";
case HTTP_0_9:
default:
return "HTTP/0.9";
}
}
/**
* Returns a char segment containing the protocol.
*/
public CharSegment getProtocolBuffer()
{
return _protocol;
}
/**
* Returns the HTTP version of the request based on getProtocol().
*/
int getVersion()
{
if (_version > 0) {
return _version;
}
CharSegment protocol = getProtocolBuffer();
if (protocol.equals("HTTP/1.1")) {
_version = HTTP_1_1;
return HTTP_1_1;
}
else if (protocol.equals("HTTP/1.0")) {
_version = HTTP_1_0;
return _version;
}
else if (protocol.equals("HTTP/0.9")) {
_version = HTTP_0_9;
return HTTP_0_9;
}
else if (protocol.length() < 8) {
_version = HTTP_0_9;
return _version;
}
int i = protocol.indexOf('/');
int len = protocol.length();
int major = 0;
for (i++; i < len; i++) {
char ch = protocol.charAt(i);
if ('0' <= ch && ch <= '9')
major = 10 * major + ch - '0';
else if (ch == '.')
break;
else {
_version = HTTP_1_0;
return _version;
}
}
int minor = 0;
for (i++; i < len; i++) {
char ch = protocol.charAt(i);
if ('0' <= ch && ch <= '9')
minor = 10 * minor + ch - '0';
else
break;
}
_version = 256 * major + minor;
return _version;
}
//
// HTTP request headers
//
/**
* Returns the header.
*/
@Override
public String getHeader(String key)
{
CharSegment buf = getHeaderBuffer(key);
if (buf != null)
return buf.toString();
else
return null;
}
/**
* Returns the number of headers.
*/
@Override
public int getHeaderSize()
{
return _headerSize;
}
/**
* Returns the header key
*/
@Override
public CharSegment getHeaderKey(int index)
{
return _headerKeys[index];
}
/**
* Returns the header value
*/
@Override
public CharSegment getHeaderValue(int index)
{
return _headerValues[index];
}
/**
* Returns the matching header.
*
* @param testBuf header key
* @param length length of the key.
*/
public CharSegment getHeaderBuffer(char []testBuf, int length)
{
char []keyBuf = _headerBuffer;
CharSegment []headerKeys = _headerKeys;
char []toLowerAscii = _toLowerAscii;
for (int i = _headerSize - 1; i >= 0; i--) {
CharSegment key = headerKeys[i];
if (key.length() != length)
continue;
int offset = key.offset();
int j;
for (j = length - 1; j >= 0; j--) {
char a = testBuf[j];
char b = keyBuf[offset + j];
if (a == b) {
continue;
}
else if (toLowerAscii[a] != toLowerAscii[b]) {
break;
}
}
if (j < 0) {
return _headerValues[i];
}
}
return null;
}
/**
* Returns the header value for the key, returned as a CharSegment.
*/
@Override
public CharSegment getHeaderBuffer(String key)
{
int i = matchNextHeader(0, key);
if (i >= 0) {
return _headerValues[i];
}
else {
return null;
}
}
/**
* Fills an ArrayList with the header values matching the key.
*
* @param values ArrayList which will contain the maching values.
* @param key the header key to select.
*/
@Override
public void getHeaderBuffers(String key, ArrayList values)
{
int i = -1;
while ((i = matchNextHeader(i + 1, key)) >= 0) {
values.add(_headerValues[i]);
}
}
/**
* Return an enumeration of headers matching a key.
*
* @param key the header key to match.
* @return the enumeration of the headers.
*/
@Override
public Enumeration getHeaders(String key)
{
ArrayList values = new ArrayList();
int i = -1;
while ((i = matchNextHeader(i + 1, key)) >= 0) {
values.add(_headerValues[i].toString());
}
return Collections.enumeration(values);
}
/**
* Returns the index of the next header matching the key.
*
* @param i header index to start search
* @param key header key to match
*
* @return the index of the next header matching, or -1.
*/
private int matchNextHeader(int i, String key)
{
int size = _headerSize;
int length = key.length();
char []keyBuf = _headerBuffer;
char []toLowerAscii = _toLowerAscii;
for (; i < size; i++) {
CharSegment header = _headerKeys[i];
if (header.length() != length)
continue;
int offset = header.offset();
int j;
for (j = 0; j < length; j++) {
char a = key.charAt(j);
char b = keyBuf[offset + j];
if (a == b) {
continue;
}
else if (toLowerAscii[a] != toLowerAscii[b]) {
break;
}
}
if (j == length) {
return i;
}
}
return -1;
}
/**
* Adds a new header. Used only by the caching to simulate
* If-Modified-Since.
*
* @param key the key of the new header
* @param value the value for the new header
*/
@Override
public void setHeader(String key, String value)
{
int tail;
if (_headerSize > 0) {
tail = (_headerValues[_headerSize - 1].offset()
+ _headerValues[_headerSize - 1].length());
}
else {
tail = 0;
}
int keyLength = key.length();
int valueLength = value.length();
char []headerBuffer = _headerBuffer;
for (int i = keyLength - 1; i >= 0; i--) {
headerBuffer[tail + i] = key.charAt(i);
}
_headerKeys[_headerSize].init(headerBuffer, tail, keyLength);
tail += keyLength;
for (int i = valueLength - 1; i >= 0; i--) {
headerBuffer[tail + i] = value.charAt(i);
}
_headerValues[_headerSize].init(headerBuffer, tail, valueLength);
_headerSize++;
// XXX: size
}
//
// attribute management
//
/**
* Initialize any special attributes.
*/
@Override
protected void initAttributes(RequestFacade request)
{
ConnectionTcp conn = getConnection();
if (! (conn instanceof ConnectionTcp))
return;
ConnectionTcp tcpConn = (ConnectionTcp) conn;
if (! conn.isSecure())
return;
SocketBar socket = tcpConn.socket();
String cipherSuite = socket.cipherSuite();
request.setCipherSuite(cipherSuite);
int keySize = socket.cipherBits();
if (keySize != 0) {
request.setCipherKeySize(keySize);
}
try {
X509Certificate []certs = socket.getClientCertificates();
if (certs != null && certs.length > 0) {
request.setCipherCertificate(certs);
}
} catch (Exception e) {
log.log(Level.FINER, e.toString(), e);
}
}
//
// stream management
//
//
// request parsing
//
@Override
public Invocation parseInvocation()
throws IOException
{
// initialize state for a request
initRequest();
if (! parseRequest()) {
return null;
}
if (isUpgrade() && upgradeHttp2()) {
return null;
}
CharSequence host = getInvocationHost();
return getInvocation(host, _uri, _uriLength);
}
/**
* Parses a http request.
*/
private boolean parseRequest()
throws IOException
{
try {
ReadBuffer is = conn().readStream();
if (! readRequest(is)) {
clearRequest();
return false;
}
if (log.isLoggable(Level.FINE)) {
log.fine(_method + " "
+ new String(_uri, 0, _uriLength) + " " + _protocol
+ " (" + dbgId() + ")");
log.fine("Remote-IP: " + remoteHost()
+ ":" + getRemotePort() + " (" + dbgId() + ")");
}
parseHeaders(is);
return true;
} catch (ClientDisconnectException e) {
throw e;
} catch (SocketTimeoutException e) {
log.log(Level.FINER, e.toString(), e);
return false;
} catch (ArrayIndexOutOfBoundsException e) {
log.log(Level.FINEST, e.toString(), e);
throw new BadRequestException(L.l("Invalid request: URL or headers are too long"), e);
} catch (Throwable e) {
log.log(Level.FINEST, e.toString(), e);
throw new BadRequestException(String.valueOf(e), e);
}
}
/*
public void onAccept()
{
startRequest();
}
*/
/**
* Clear the request variables in preparation for a new request.
*
* @param s the read stream for the request
*/
@Override
protected void initRequest()
{
super.initRequest();
// HttpBufferStore httpBuffer = getHttpBufferStore();
HttpBufferStore bufferStore = getHttpBufferStore();
_method.clear();
_methodString = null;
_protocol.clear();
_uriLength = 0;
if (bufferStore == null) {
// server/05h8
_uri = getSmallUriBuffer(); // httpBuffer.getUriBuffer();
_headerBuffer = getSmallHeaderBuffer(); // httpBuffer.getHeaderBuffer();
_headerKeys = getSmallHeaderKeys(); // httpBuffer.getHeaderKeys();
_headerValues = getSmallHeaderValues(); // httpBuffer.getHeaderValues();
}
_uriHost.clear();
_host = null;
_keepalive = KeepaliveState.INIT;
_headerSize = 0;
_headerLength = 0;
_inOffset = 0;
_isChunkedIn = false;
}
/**
* Read the first line of a request:
*
* GET [http://www.caucho.com[:80]]/path [HTTP/1.x]
*
* @return true if the request is valid
*/
private boolean readRequest(ReadBuffer is)
throws IOException
{
// server/12o3 - default to 1.0 for error messages in request
_version = HTTP_1_0;
boolean []isHttpWhitespace = _isHttpWhitespace;
char []toUpperAscii = _toUpperAscii;
byte []readBuffer = is.buffer();
int readOffset = is.offset();
int readLength = is.length();
int ch;
// skip leading whitespace
do {
if (readLength <= readOffset) {
if ((readLength = is.fillBuffer()) < 0) {
return false;
}
readOffset = 0;
}
ch = readBuffer[readOffset++] & 0xff;
} while (isHttpWhitespace[ch]);
char []buffer = _method.buffer();
int length = buffer.length;
int offset = 0;
// scan method
while (true) {
if (length <= offset) {
}
else if (ch > ' ') {
buffer[offset++] = toUpperAscii[ch];
}
else {
break;
}
if (readLength <= readOffset) {
if ((readLength = is.fillBuffer()) < 0)
return false;
readOffset = 0;
}
ch = readBuffer[readOffset++];
}
_method.length(offset);
// skip whitespace
while (ch == ' ' || ch == '\t') {
if (readLength <= readOffset) {
if ((readLength = is.fillBuffer()) < 0)
return false;
readOffset = 0;
}
ch = readBuffer[readOffset++];
}
if (ch == '*') {
if (_method.matches("PRI") && startHttp2()) {
return false;
}
else if (_method.matches("HAMP")) {
if (! _state.startBartender()) {
return false;
}
else {
log.warning("Invalid Request: unable to start HAMP");
throw new BadRequestException("Invalid Request(Remote IP=" + remoteHost() + ")");
}
}
}
byte []uriBuffer = _uri;
int uriLength = 0;
// skip 'http:'
if (ch != '/') {
while (ch > ' ' && ch != '/') {
if (readLength <= readOffset) {
if ((readLength = is.fillBuffer()) < 0)
return false;
readOffset = 0;
}
ch = readBuffer[readOffset++];
}
if (ch == '/') {
}
else {
log.warning("Invalid Request (method='" + _method + "' url ch=0x" + Integer.toHexString(ch & 0xff)
+ " off=" +readOffset + " len=" + readLength
+ ") (IP=" + remoteHost() + ")");
log.warning(new String(readBuffer, 0, readLength));
throw new BadRequestException("Invalid Request(Remote IP=" + remoteHost() + ")");
}
if (readLength <= readOffset) {
if ((readLength = is.fillBuffer()) < 0) {
if (ch == '/') {
uriBuffer[uriLength++] = (byte) ch;
_uriLength = uriLength;
}
_version = 0;
return true;
}
readOffset = 0;
}
int ch1 = readBuffer[readOffset++] & 0xff;
if (ch1 != '/') {
uriBuffer[uriLength++] = (byte) ch;
ch = ch1;
}
else {
// read host
host:
while (true) {
if (readLength <= readOffset) {
if ((readLength = is.fillBuffer()) < 0) {
_version = 0;
return true;
}
readOffset = 0;
}
ch = readBuffer[readOffset++] & 0xff;
switch (ch) {
case ' ': case '\t': case '\n': case '\r':
break host;
case '?':
break host;
case '/':
break host;
default:
_uriHost.append((char) ch);
break;
}
}
}
}
int readTail = uriBuffer.length - uriLength - 1;
readTail = Math.min(readTail, readLength);
// read URI
while (! isHttpWhitespace[ch]) {
// There's no check for over-running the length because
// allowing resizing would allow a DOS memory attack and
// also lets us save a bit of efficiency.
uriBuffer[uriLength++] = (byte) ch;
if (readTail <= readOffset) {
if ((readTail = fillUrlTail(is, readOffset, uriLength)) <= 0) {
_uriLength = uriLength;
_version = 0;
return true;
}
readOffset = is.offset();
uriBuffer = _uri;
uriLength = _uriLength;
}
ch = readBuffer[readOffset++] & 0xff;
}
_uriLength = uriLength;
_version = 0;
// skip whitespace
while (ch == ' ' || ch == '\t') {
if (readLength <= readOffset) {
readOffset = 0;
if ((readLength = is.fillBuffer()) < 0)
return true;
}
ch = readBuffer[readOffset++] & 0xff;
}
buffer = _protocol.buffer();
length = buffer.length;
offset = 0;
// scan protocol
while (! isHttpWhitespace[ch]) {
if (offset < length) {
buffer[offset++] = toUpperAscii[ch];
}
if (readLength <= readOffset) {
readOffset = 0;
if ((readLength = is.fillBuffer()) < 0) {
_protocol.length(offset);
return true;
}
}
ch = readBuffer[readOffset++] & 0xff;
}
_protocol.length(offset);
if (offset != 8) {
_protocol.append("HTTP/0.9");
_version = HTTP_0_9;
killKeepalive("0.9");
}
else if (buffer[7] == '1') { // && _protocol.equals(_http11Cb))
_version = HTTP_1_1;
}
else if (buffer[7] == '0') { // && _protocol.equals(_http10Cb))
_version = HTTP_1_0;
}
else {
_version = HTTP_0_9;
killKeepalive("0.9");
}
// skip to end of line
while (ch != '\n') {
if (readLength <= readOffset) {
if ((readLength = is.fillBuffer()) < 0)
return true;
readOffset = 0;
}
ch = readBuffer[readOffset++];
}
is.offset(readOffset);
return true;
}
//
// body reading
//
/*
protected void setHmuxRequest(HmuxRequest hmuxRequest)
{
_hmuxRequest = hmuxRequest;
}
*/
@Override
public boolean readBodyChunk()
throws IOException
{
long inOffset = _inOffset;
if (inOffset == 0) {
if (! initBody()) {
_state.onBodyComplete();
return false;
}
}
if (_isChunkedIn) {
return readChunk();
}
else {
return readContentLength();
}
}
private boolean readChunk()
throws IOException
{
long inOffset = _inOffset;
if (inOffset == 0) {
inOffset = readChunkHeader();
}
if (inOffset < 0) {
return false;
}
ReadBuffer is = conn().readStream();
int sublen = (int) Math.min(Integer.MAX_VALUE, inOffset);
sublen = Math.min(sublen, is.availableBuffer());
if (sublen == 0) {
return true;
}
if (sublen <= TempBuffer.SMALL_SIZE) {
TempBuffer tBuf = TempBuffer.allocateSmall();
int readlen = is.readAll(tBuf.buffer(), 0, sublen);
if (readlen != sublen) {
throw new IllegalStateException();
}
_inOffset -= sublen;
tBuf.length(sublen);
_state.onBodyChunk(tBuf);
return true;
}
else if (sublen <= TempBuffer.SIZE) {
TempBuffer tBuf = TempBuffer.allocate();
int readlen = is.readAll(tBuf.buffer(), 0, sublen);
if (readlen != sublen) {
throw new IllegalStateException();
}
_inOffset -= sublen;
tBuf.length(sublen);
_state.onBodyChunk(tBuf);
return true;
}
else {
// XXX: should allow multiple reads -- although Buffer will fix
TempBuffer tBuf = TempBuffer.allocate();
int readlen = is.readAll(tBuf.buffer(), 0, sublen);
if (readlen != sublen) {
throw new IllegalStateException();
}
_inOffset -= sublen;
tBuf.length(sublen);
_state.onBodyChunk(tBuf);
// more data expected
return true;
}
}
private int readChunkHeader()
throws IOException
{
ReadBuffer is = conn().readStream();
int ch;
if ((ch = is.read()) == '\r') {
if ((ch = is.read()) == '\n') {
ch = is.read();
}
}
else if (ch == '\n') {
ch = is.read();
}
int len = readChunkLen(is, ch);
if (len <= 0) {
_state.onBodyComplete();
len = -1;
}
_inOffset = len;
return len;
}
private int readChunkLen(ReadBuffer is, int ch)
throws IOException
{
int len = 0;
while (true) {
if ('0' <= ch && ch <= '9') {
len = len * 16 + ch - '0';
}
else if ('a' <= ch && ch <= 'f') {
len = len * 16 + ch - 'a' + 10;
}
else if ('A' <= ch && ch <= 'F') {
len = len * 16 + ch - 'A' + 10;
}
else if (ch == '\r') {
if ((ch = is.read()) == '\n') {
return len;
}
}
else if (ch == '\n') {
return len;
}
else {
throw new BadRequestException(L.l("Invalid chunk length"));
}
ch = is.read();
}
}
private boolean readContentLength()
throws IOException
{
long inOffset = _inOffset;
long contentLength = contentLength();
if (contentLength <= inOffset) {
_state.onBodyComplete();
return false;
}
ReadBuffer is = conn().readStream();
int sublen = (int) Math.min(Integer.MAX_VALUE, (contentLength - inOffset));
sublen = Math.min(sublen, is.availableBuffer());
if (sublen == 0) {
return true;
}
if (sublen <= TempBuffer.SMALL_SIZE) {
TempBuffer tBuf = TempBuffer.allocateSmall();
int readlen = is.read(tBuf.buffer(), 0, sublen);
if (readlen != sublen) {
throw new IllegalStateException();
}
_inOffset += sublen;
tBuf.length(sublen);
_state.onBodyChunk(tBuf);
if (contentLength <= _inOffset) {
_state.onBodyComplete();
}
return false;
}
else if (sublen <= TempBuffer.SIZE) {
TempBuffer tBuf = TempBuffer.allocate();
int readlen = is.read(tBuf.buffer(), 0, sublen);
if (readlen != sublen) {
throw new IllegalStateException();
}
_inOffset += sublen;
tBuf.length(sublen);
_state.onBodyChunk(tBuf);
if (contentLength <= _inOffset) {
_state.onBodyComplete();
}
return false;
}
else {
// XXX: should allow multiple reads -- although Buffer will fix
TempBuffer tBuf = TempBuffer.allocate();
int readlen = is.read(tBuf.buffer(), 0, sublen);
if (readlen != sublen) {
throw new IllegalStateException();
}
_inOffset += sublen;
tBuf.length(sublen);
_state.onBodyChunk(tBuf);
// more data expected
return true;
}
}
private boolean initBody()
throws IOException
{
// _hmuxRequest = null;
long contentLength = contentLength();
if (contentLength < 0 && HTTP_1_1 <= getVersion()
&& getHeader("Transfer-Encoding") != null) {
_isChunkedIn = true;
return true;
}
// Otherwise use content-length
else if (contentLength >= 0) {
return true;
}
else if (getMethod().equals("POST")) {
_inOffset = Long.MAX_VALUE;
throw new BadRequestException("POST requires content-length");
}
else {
_inOffset = Long.MAX_VALUE;
return false;
}
}
//
// upgrades
//
protected boolean startHmux()
{
return false;
}
private boolean upgradeHttp2() throws IOException
{
String connHeader = getHeader("Connection");
String upgradeHeader = getHeader("Upgrade");
if (! "h2-12".equals(upgradeHeader)
|| connHeader.indexOf("HTTP2-Settings") < 0) {
return false;
}
String settings = getHeader("HTTP2-Settings");
if (settings == null) {
return false;
}
RequestProtocolHttp2 handler;
handler = new RequestProtocolHttp2(protocolHttp(),
http(),
getConnection());
startDuplex(handler);
handler.onStartUpgrade(this);
return true;
}
private boolean startHttp2()
throws IOException
{
ReadBuffer is = conn().readStream();
//int offset = is.getOffset();
byte []header = new byte[24];
if (is.readAll(header, 0, header.length) != header.length) {
// XXX: rewind
return false;
}
if (! Arrays.equals(header, Http2Constants.CONNECTION_HEADER)) {
return false;
}
RequestProtocolHttp2 handler;
handler = new RequestProtocolHttp2(protocolHttp(),
http(),
getConnection());
startDuplex(handler);
handler.onStart();
return true;
}
/**
* Parses headers from the read stream.
*
* @param s the input read stream
*/
private void parseHeaders(ReadBuffer s) throws IOException
{
int version = getVersion();
if (version < HTTP_1_0) {
return;
}
if (version < HTTP_1_1) {
killKeepalive("http client version less than 1.1: " + version);
}
byte []readBuffer = s.buffer();
int readOffset = s.offset();
int readLength = s.length();
char []headerBuffer = _headerBuffer;
headerBuffer[0] = 'z';
int headerOffset = 1;
_headerSize = 0;
int readTail = readLength;
if (headerBuffer.length - 1 < readTail - readOffset) {
readTail = headerBuffer.length - 1;
}
boolean isLogFine = log.isLoggable(Level.FINE);
while (true) {
int ch;
int keyOffset = headerOffset;
// scan the key
while (true) {
if (readTail <= readOffset) {
if ((readTail = fillHeaderTail(s, readOffset, headerOffset)) <= 0) {
return;
}
readOffset = s.offset();
headerOffset = _headerLength;
headerBuffer = _headerBuffer;
}
ch = readBuffer[readOffset++];
if (ch == '\n') {
s.offset(readOffset);
return;
}
else if (ch == ':') {
break;
}
headerBuffer[headerOffset++] = (char) ch;
}
// strip trailing whitespace from key
while (headerBuffer[headerOffset - 1] == ' ') {
headerOffset--;
}
int keyLength = headerOffset - keyOffset;
// skip whitespace
do {
if (readTail <= readOffset) {
if ((readTail = fillHeaderTail(s, readOffset, headerOffset)) <= 0) {
return;
}
readOffset = s.offset();
headerOffset = _headerLength;
headerBuffer = _headerBuffer;
}
ch = readBuffer[readOffset++];
} while (ch == ' ' || ch == '\t');
int valueOffset = headerOffset;
// scan the value
while (true) {
if (readTail <= readOffset) {
if ((readTail = fillHeaderTail(s, readOffset, headerOffset)) <= 0) {
break;
}
readOffset = s.offset();
headerOffset = _headerLength;
headerBuffer = _headerBuffer;
}
if (ch == '\n') {
int ch1 = readBuffer[readOffset];
if (ch1 == ' ' || ch1 == '\t') {
ch = ' ';
readOffset++;
if (headerBuffer[headerOffset - 1] == '\r') {
headerOffset--;
}
}
else
break;
}
headerBuffer[headerOffset++] = (char) ch;
ch = readBuffer[readOffset++];
}
while (headerBuffer[headerOffset - 1] <= ' ') {
headerOffset--;
}
int headerSize = _headerSize;
CharSegment []headerKeys = _headerKeys;
CharSegment []headerValues = _headerValues;
if (headerKeys.length <= headerSize) {
_headerLength = headerOffset;
extendHeaderBuffers();
headerBuffer = _headerBuffer;
headerKeys = _headerKeys;
headerValues = _headerValues;
}
headerKeys[headerSize].init(headerBuffer, keyOffset, keyLength);
int valueLength = headerOffset - valueOffset;
headerValues[headerSize].init(headerBuffer, valueOffset, valueLength);
if (isLogFine) {
log.fine(headerKeys[headerSize] + ": " + headerValues[headerSize]
+ " (" + dbgId() + ")");
}
if (addHeaderInt(headerBuffer, keyOffset, keyLength,
headerValues[headerSize])) {
headerSize++;
}
_headerSize = headerSize;
}
}
private int fillUrlTail(ReadBuffer s, int readOffset,
int uriOffset)
throws IOException
{
_uriLength = uriOffset;
if (_uri.length <= uriOffset) {
extendHeaderBuffers();
}
if (s.length() <= readOffset) {
if (s.fillBuffer() < 0) {
return -1;
}
}
else {
s.offset(readOffset);
}
int tail = s.length() - s.offset();
if (_uri.length - uriOffset < tail) {
tail = _uri.length - uriOffset;
}
return tail;
}
private int fillHeaderTail(ReadBuffer s, int readOffset,
int headerOffset)
throws IOException
{
_headerLength = headerOffset;
if (_headerBuffer.length <= headerOffset) {
extendHeaderBuffers();
}
if (s.length() <= readOffset) {
if (s.fillBuffer() < 0) {
return -1;
}
}
else {
s.offset(readOffset);
}
int tail = s.length() - s.offset();
if (_headerBuffer.length - headerOffset < tail) {
tail = _headerBuffer.length - headerOffset;
}
return tail;
}
protected void extendHeaderBuffers()
throws IOException
{
HttpBufferStore bufferStore = getHttpBufferStore();
if (bufferStore != null) {
throw new BadRequestException(L.l("URL or HTTP headers are too long (IP={0})",
getRemoteAddr()));
}
bufferStore = allocateHttpBufferStore();
byte []uri = bufferStore.getUriBuffer();
System.arraycopy(_uri, 0, uri, 0, _uriLength);
char []headerBuffer = bufferStore.getHeaderBuffer();
CharSegment []headerKeys = bufferStore.getHeaderKeys();
CharSegment []headerValues = bufferStore.getHeaderValues();
if (headerBuffer == _headerBuffer || _uri == uri) {
throw new IllegalStateException();
}
System.arraycopy(_headerBuffer, 0, headerBuffer, 0, _headerLength);
for (int i = 0; i < _headerSize; i++) {
headerKeys[i].init(headerBuffer,
_headerKeys[i].offset(),
_headerKeys[i].length());
headerValues[i].init(headerBuffer,
_headerValues[i].offset(),
_headerValues[i].length());
}
_uri = uri;
_headerBuffer = headerBuffer;
_headerKeys = headerKeys;
_headerValues = headerValues;
}
@Override
public void killKeepalive(String msg)
{
super.killKeepalive(msg);
if (_keepalive == KeepaliveState.ALLOC) {
// XXX: free
}
_keepalive = KeepaliveState.KILL;
}
/*
@Override
public boolean isKeepalive()
{
switch (_keepalive) {
case KILL:
return false;
case ALLOC:
return true;
case INIT:
// XXX: try to allocate
return true;
default:
return false;
}
}*/
/**
* Handles a timeout.
*/
@Override
public void onTimeout()
{
try {
//request().sendError(WebRequest.INTERNAL_SERVER_ERROR);
if (true) throw new UnsupportedOperationException();
} catch (Exception e) {
log.log(Level.FINEST, e.toString(), e);
}
closeResponse();
}
public void onCloseRead()
{
}
@Override
public void onCloseConnection()
{
super.onCloseConnection();
_headerBuffer = null;
_headerKeys = null;
_headerValues = null;
}
//
// upgrade to duplex
//
/*
@Override
public boolean isSuspend()
{
return getRequestProtocol().isSuspend();
}
*/
@Override
public boolean isDuplex()
{
throw new UnsupportedOperationException();
//return getRequestProtocol().isDuplex();
}
@Override
public void startDuplex(ConnectionProtocol request)
{
connHttp().request(request);
}
/**
* Cleans up at the end of the invocation
*/
/*
//@Override
public void finishRequest()
throws IOException
{
//super.finishRequest();
skip();
}
*/
@Override
protected String dbgId()
{
HttpContainer httpSystem = http();
String serverId = "";
if (httpSystem != null) {
serverId = httpSystem.getServerDisplayName();
}
long connId = connectionId();
if ("".equals(serverId))
return "Http[" + connId + "] ";
else
return "Http[" + serverId + ", " + connId + "] ";
}
public String getDebugId()
{
HttpContainer httpSystem = http();
String serverId = "";
if (httpSystem != null) {
serverId = httpSystem.getServerDisplayName();
}
long connId = connectionId();
if ("".equals(serverId))
return "" + connId + "";
else
return "" + serverId + ", " + connId + "";
}
@Override
protected OutResponseBase createOut()
{
//RequestHttp request = (RequestHttp) getRequest();
return new OutResponseHttp(this);
}
/*
OutHttpProxy getProxy()
{
return _outProxy;
}
*/
boolean isChunked()
{
return getOut().isChunkedEncoding();
}
/*
private WriteStream getRawWrite()
{
return _rawWrite;
}
*/
public boolean calculateChunkedEncoding()
{
//RequestFacade request = request();
if (RequestHttp.HTTP_1_1 <= getVersion()
&& contentLengthOut() < 0
&& ! getMethod().equalsIgnoreCase("HEAD")) {
return true;
}
else {
return false;
}
}
@Override
public boolean canWrite(long sequence)
{
return _state.sequence() <= sequence;
}
@Override
public boolean writeFirst(WriteBuffer out,
TempBuffer head, long length, boolean isEnd)
{
//RequestFacade request = request();
//System.out.println("WRI: " + head + " " + request);
if (_state.isPrevCloseWrite()) {
return writeFirstImpl(out, head, length, isEnd);
}
else {
saveFirst(out, head, length, isEnd);
return false;
}
}
private boolean writeFirstImpl(WriteBuffer out,
TempBuffer head,
long length,
boolean isEnd)
{
try {
writeHeaders(out, isEnd ? length : -1);
if (head != null) {
if (isChunked()) {
writeFirstChunk(out, length);
}
return writeNextImpl(out, head, isEnd);
}
else {
if (isEnd && ! isKeepalive()) {
closeWrite();
// out.close();
return true;
}
else if (isEnd) {
closeWrite();
}
return false;
}
} catch (Throwable e) {
e.printStackTrace();
log.log(Level.WARNING, e.toString(), e);
disconnect(out);
return true;
}
}
@Override
public boolean writeNext(WriteBuffer out, TempBuffer head, boolean isEnd)
{
//RequestFacade request = request();
if (_state.isPrevCloseWrite()) {
return writeNextImpl(out, head, isEnd);
}
else {
saveNext(out, head, isEnd);
return false;
}
}
private boolean writeNextImpl(WriteBuffer out,
TempBuffer head,
boolean isEnd)
{
TempBuffer next;
try {
for (; head != null; head = next) {
next = head.getNext();
try {
out.write(head.buffer(), 0, head.length());
} catch (IOException e) {
log.log(Level.WARNING, e.toString(), e);
}
if (next != null) {
head.setNext(null);
head.freeSelf();
}
else if (! isEnd) {
head.freeSelf();
}
}
if (isEnd) {
if (isChunked()) {
writeLastChunk(out);
}
if (isKeepalive()) {
//out.flush();
}
else {
//out.close();
return true;
}
//getConnection().proxy().requestWake();
}
else {
//out.flush();
}
return false;
} catch (IOException e) {
log.log(Level.WARNING, e.toString(), e);
return true;
} finally {
if (isEnd) {
closeWrite();
}
}
}
@Override
protected void closeWrite()
{
_state.onCloseWrite();
/*
RequestFacade request = _request;
_request = null;
if (request != null) {
request.onCloseWrite(next());
}
*/
//_state.onCloseWrite(next());
}
private void saveFirst(WriteBuffer out,
TempBuffer head,
long length,
boolean isEnd)
{
_pending = new PendingFirst(out, head, length, isEnd);
}
private void saveNext(WriteBuffer out,
TempBuffer head,
boolean isEnd)
{
_pending.next(new PendingNext(out, head, isEnd));
}
@Override
public void writePending()
{
PendingFirst pending = _pending;
_pending = null;
if (pending != null) {
pending.write();
}
}
abstract private class Pending
{
private Pending _next;
void next(Pending next)
{
if (_next != null) {
_next.next(next);
}
else {
_next = next;
}
}
void writeNext()
{
Pending next = _next;
if (next != null) {
next.write();
}
}
abstract void write();
}
private class PendingFirst extends Pending
{
private WriteBuffer _out;
private TempBuffer _head;
private long _length;
private boolean _isEnd;
PendingFirst(WriteBuffer out,
TempBuffer head,
long length,
boolean isEnd)
{
_out = out;
_head = head;
_length = length;
_isEnd = isEnd;
}
@Override
void write()
{
writeFirstImpl(_out, _head, _length, _isEnd);
writeNext();
}
}
private class PendingNext extends Pending
{
private WriteBuffer _out;
private TempBuffer _head;
private boolean _isEnd;
PendingNext(WriteBuffer out,
TempBuffer head,
boolean isEnd)
{
_out = out;
_head = head;
_isEnd = isEnd;
}
@Override
void write()
{
writeNextImpl(_out, _head, _isEnd);
writeNext();
}
}
private void writeFirstChunk(WriteBuffer os, long length)
throws IOException
{
os.write('\r');
os.write('\n');
os.write(toHex(length >> 12));
os.write(toHex(length >> 8));
os.write(toHex(length >> 4));
os.write(toHex(length >> 0));
os.write('\r');
os.write('\n');
}
private void writeLastChunk(WriteBuffer os)
throws IOException
{
os.write('\r');
os.write('\n');
os.write('0');
os.write('\r');
os.write('\n');
os.write('\r');
os.write('\n');
}
private static int toHex(long v)
{
v = v & 0xf;
if (v < 0xa) {
return (int) (v + '0');
}
else {
return (int) (v - 10 + 'a');
}
}
@Override
public void upgrade()
{
if (isOutCommitted()) {
throw new IllegalStateException();
}
_isUpgrade = true;
getOut().upgrade();
}
/**
* Upgrade protocol
*/
/*
@Override
public DuplexController upgradeProtocol(DuplexHandler handler)
{
TcpConnection conn
= (TcpConnection) ((TcpServerRequest) getRequest()).getConnection();
TcpDuplexController controller = toDuplex(handler);
HttpServletResponseImpl response = _request.getResponseFacade();
response.setStatus(101);
setContentLength(0);
if (log.isLoggable(Level.FINE))
log.fine(this + " upgrade HTTP to " + handler);
return controller;
}
*/
/**
* Writes the 100 continue response.
*/
@Override
protected void writeContinueInt()
throws IOException
{
if (true) throw new UnsupportedOperationException();
/*
WriteStream os = getRawWrite();
os.print("HTTP/1.1 100 Continue\r\n\r\n");
os.flush();
*/
}
/**
* Implementation to write the HTTP headers. If the length is positive,
* it's a small request where the buffer contains the entire request,
* so the length is already known.
*
* @param os the output stream to write the headers to.
* @param length if non-negative, the length of the entire request.
*
* @return true if the data in the request should use chunked encoding.
*/
//@Override
private void writeHeaders(WriteBuffer os, long length)
throws IOException
{
int version = getVersion();
boolean debug = log.isLoggable(Level.FINE);
if (version < RequestHttp.HTTP_1_0) {
killKeepalive("http client version " + version);
return;
}
long contentLength = contentLengthOut();
int statusCode = status();
if (statusCode == 200) {
if (version < RequestHttp.HTTP_1_1) {
os.write(_http10ok, 0, _http10ok.length);
}
else {
os.write(_http11ok, 0, _http11ok.length);
}
}
else {
if (version < RequestHttp.HTTP_1_1) {
os.printLatin1("HTTP/1.0 ");
}
else {
os.printLatin1("HTTP/1.1 ");
}
os.write((statusCode / 100) % 10 + '0');
os.write((statusCode / 10) % 10 + '0');
os.write(statusCode % 10 + '0');
os.write(' ');
os.printLatin1(statusMessage());
}
//String contentType = contentType();
if (debug) {
log.fine("HTTP/1.1 " +
statusCode + " " + statusMessage() + " (" + dbgId() + ")");
}
if (_isUpgrade) {
String upgrade = headerOut("Upgrade");
if (upgrade != null) {
os.printLatin1("\r\nUpgrade: ");
os.printLatin1NoLf(upgrade);
}
os.printLatin1("\r\nConnection: Upgrade");
killKeepalive("duplex/upgrade");
if (debug) {
log.fine(dbgId() + "Connection: Upgrade");
}
}
String serverHeader = serverHeader();
if (serverHeader == null) {
os.write(_serverHeaderBytes, 0, _serverHeaderBytes.length);
}
else {
os.printLatin1("\r\nServer: ");
os.printLatin1NoLf(serverHeader);
}
/*
if (statusCode >= 400) {
removeHeader("ETag");
removeHeader("Last-Modified");
}
else if (statusCode == HttpConstants.SC_NOT_MODIFIED
|| statusCode == HttpConstants.SC_NO_CONTENT) {
// php/1b0k
contentType = null;
}
else if (httpRequest.isCacheControl()) {
// application manages cache control
}
else if (httpRequest.isNoCache()) {
// server/1b15
removeHeader("ETag");
removeHeader("Last-Modified");
// even in case of 302, this may be needed for filters which
// automatically set cache headers
setHeaderOutImpl("Expires", "Thu, 01 Dec 1994 16:00:00 GMT");
os.printLatin1("\r\nCache-Control: no-cache");
if (debug) {
log.fine("Cache-Control: no-cache (" + dbgId() + ")");
}
}
else if (httpRequest.isNoCacheUnlessVary()
&& ! containsHeaderOut("Vary")) {
os.printLatin1("\r\nCache-Control: private");
if (debug) {
log.fine("Cache-Control: private (" + dbgId() + ")");
}
}
else if (httpRequest.isPrivateCache()) {
if (RequestHttp.HTTP_1_1 <= version) {
// technically, this could be private="Set-Cookie,Set-Cookie2"
// but caches don't recognize it, so there's no real extra value
os.printLatin1("\r\nCache-Control: private");
if (debug)
log.fine("Cache-Control: private (" + dbgId() + ")");
}
else {
setHeaderOutImpl("Expires", "Thu, 01 Dec 1994 16:00:00 GMT");
os.printLatin1("\r\nCache-Control: no-cache");
if (debug) {
log.fine("CacheControl: no-cache (" + dbgId() + ")");
}
}
}
*/
ArrayList headerKeys = headerKeysOut();
ArrayList headerValues = headerValuesOut();
int size = headerKeys.size();
for (int i = 0; i < size; i++) {
String key = headerKeys.get(i);
if (_isUpgrade && "Upgrade".equalsIgnoreCase(key)) {
continue;
}
os.write('\r');
os.write('\n');
os.printLatin1NoLf(key);
os.write(':');
os.write(' ');
os.printLatin1NoLf(headerValues.get(i));
if (debug) {
log.fine(key + ": " + headerValues.get(i) + " (" + dbgId() + ")");
}
}
writeCookies(os);
/*
httpRequest.writeCookies(os);
if (contentType != null) {
// server/1b5a
if (charEncoding == null && contentType.startsWith("text/")) {
// if (webApp != null)
// charEncoding = webApp.getCharacterEncoding();
// always use a character encoding to avoid XSS attacks (?)
if (charEncoding == null) {
charEncoding = "utf-8";
}
}
os.write(_contentTypeBytes, 0, _contentTypeBytes.length);
os.printLatin1(contentType);
if (charEncoding != null) {
os.write(_charsetBytes, 0, _charsetBytes.length);
os.printLatin1(charEncoding);
}
if (debug) {
log.fine("Content-Type: " + contentType
+ "; charset=" + charEncoding + " (" + dbgId() + ")");
}
}
*/
if (hasFooter()) {
contentLength = -1;
length = -1;
}
//boolean hasContentLength = false;
/*
if (isHead()) {
// server/269t, server/0560
hasContentLength = true;
os.write(_contentLengthBytes, 0, _contentLengthBytes.length);
os.print(0);
}
else
*/
if (contentLength >= 0) {
os.write(_contentLengthBytes, 0, _contentLengthBytes.length);
os.print(contentLength);
//hasContentLength = true;
if (debug) {
log.fine("Content-Length: " + contentLength + " (" + dbgId() + ")");
}
}
else if (statusCode == HttpConstants.SC_NOT_MODIFIED) {
// #3089
// In the HTTP spec, a 304 has no message body so the content-length
// is not needed. The content-length is not explicitly forbidden,
// but does cause problems with certain clients.
//hasContentLength = true;
setHead();
}
else if (statusCode == HttpConstants.SC_NO_CONTENT) {
//hasContentLength = true;
os.write(_contentLengthBytes, 0, _contentLengthBytes.length);
os.print(0);
setHead();
if (debug)
log.fine("Content-Length: 0 (" + dbgId() + ")");
}
else if (length >= 0) {
os.write(_contentLengthBytes, 0, _contentLengthBytes.length);
os.print(length);
//hasContentLength = true;
if (debug) {
log.fine("Content-Length: " + length + " (" + dbgId() + ")");
}
}
if (version < RequestHttp.HTTP_1_1) {
killKeepalive("http response version: " + version);
}
else {
/* XXX: the request processing already processed this header
CharSegment conn = _request.getHeaderBuffer(_connectionCb,
_connectionCb.length);
if (conn != null && conn.equalsIgnoreCase(_closeCb)) {
_request.killKeepalive();
}
else
*/
if (isKeepalive()) {
}
else if (_isUpgrade) {
killKeepalive("http response upgrade");
}
else {
os.write(_connectionCloseBytes, 0, _connectionCloseBytes.length);
if (debug)
log.fine(dbgId() + "Connection: close");
}
}
long now = CurrentTime.currentTime();
byte []dateBuffer = fillDateBuffer(now);
int dateBufferLength = getDateBufferLength();
if (isChunked()) {
if (debug) {
log.fine(dbgId() + "Transfer-Encoding: chunked");
}
os.printLatin1("\r\nTransfer-Encoding: chunked");
os.write(dateBuffer, 0, dateBufferLength - 2);
}
else {
os.write(dateBuffer, 0, dateBufferLength);
}
}
private void writeCookies(WriteBuffer os) throws IOException
{
ArrayList cookiesOut = cookiesOut();
if (cookiesOut == null) {
return;
}
for (CookieWeb cookie : cookiesOut) {
printCookie(os, cookie);
}
}
private void printCookie(WriteBuffer os, CookieWeb cookie)
throws IOException
{
os.print("\r\nSet-Cookie: ");
os.print(cookie.name());
os.print("=");
os.print(cookie.value());
if (cookie.domain() != null) {
os.print("; Domain=");
os.print(cookie.domain());
}
if (cookie.path() != null) {
os.print("; Path=");
os.print(cookie.path());
}
if (cookie.httpOnly()) {
os.print("; HttpOnly");
}
if (cookie.secure()) {
os.print("; Secure");
}
}
@Override
public void freeSelf()
{
System.out.println("NOPE_FREE:");
//protocolHttp().requestFree(this);
}
@Override
public String toString()
{
HttpContainer httpSystem = http();
String serverId;
if (httpSystem != null)
serverId = httpSystem.getServerDisplayName();
else {
serverId = "server";
}
long connId = connectionId();
if ("".equals(serverId))
return getClass().getSimpleName() + "[" + connId + "]";
else {
return getClass().getSimpleName() + ("[" + serverId + ", " + connId + "]");
}
}
enum KeepaliveState {
INIT,
ALLOC,
KILL;
}
static {
_toLowerAscii = new char[256];
for (int i = 0; i < 256; i++) {
if ('A' <= i && i <= 'Z') {
_toLowerAscii[i] = (char) (i + 'a' - 'A');
}
else {
_toLowerAscii[i] = (char) i;
}
}
_toUpperAscii = new char[256];
for (int i = 0; i < 256; i++) {
if ('a' <= i && i <= 'z') {
_toUpperAscii[i] = (char) (i + 'A' - 'a');
}
else {
_toUpperAscii[i] = (char) i;
}
}
_isHttpWhitespace = new boolean[256];
_isHttpWhitespace[' '] = true;
_isHttpWhitespace['\t'] = true;
_isHttpWhitespace['\r'] = true;
_isHttpWhitespace['\n'] = true;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy