WebApp webApp = request.getWebApp();
String contentType = response.getContentTypeImpl();
String charEncoding = response.getCharacterEncodingImpl();
WriteStream os = getRawWrite();
int statusCode = response.getStatus();
if (statusCode == 200) {
if (version < HttpRequest.HTTP_1_1)
os.write(_http10ok, 0, _http10ok.length);
else
os.write(_http11ok, 0, _http11ok.length);
} else {
if (version < HttpRequest.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(response.getStatusMessage());
}
if (debug) {
log.fine(_request.dbgId() + "HTTP/1.1 " +
statusCode + " " + response.getStatusMessage());
}
boolean isUpgrade = false;
if (tcpConn != null && tcpConn.isDuplex()) {
isUpgrade = true;
String upgrade = getHeader("Upgrade");
if (upgrade != null) {
os.printLatin1("\r\nUpgrade: ");
os.printLatin1NoLf(upgrade);
}
os.printLatin1("\r\nConnection: Upgrade");
_request.killKeepalive("duplex/upgrade");
if (debug)
log.fine(_request.dbgId() + "Connection: Upgrade");
}
if (! containsHeader("Server")) {
os.write(_resinServerBytes, 0, _resinServerBytes.length);
}
if (statusCode >= 400) {
removeHeader("ETag");
removeHeader("Last-Modified");
}
else if (statusCode == HttpServletResponse.SC_NOT_MODIFIED
|| statusCode == HttpServletResponse.SC_NO_CONTENT) {
// php/1b0k
contentType = null;
}
else if (response.isCacheControl()) {
// application manages cache control
}
else if (response.isNoCache()) {
// server/1b15
removeHeader("ETag");
removeHeader("Last-Modified");
// even in case of 302, this may be needed for filters which
// automatically set cache headers
setHeaderImpl("Expires", "Thu, 01 Dec 1994 16:00:00 GMT");
os.printLatin1("\r\nCache-Control: no-cache");
if (debug) {
log.fine(_request.dbgId() + "" +
"Cache-Control: no-cache");
}
}
else if (response.isNoCacheUnlessVary()
&& ! containsHeader("Vary")) {
os.printLatin1("\r\nCache-Control: private");
if (debug) {
log.fine(_request.dbgId() + "Cache-Control: private");
}
}
else if (response.isPrivateCache()) {
if (HttpRequest.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(_request.dbgId() + "Cache-Control: private");
}
else {
setHeaderImpl("Expires", "Thu, 01 Dec 1994 16:00:00 GMT");
os.printLatin1("\r\nCache-Control: no-cache");
if (debug) {
log.fine(_request.dbgId() + "CacheControl: no-cache");
}
}
}
int size = _headerKeys.size();
for (int i = 0; i < size; i++) {
String key = (String) _headerKeys.get(i);
if (isUpgrade && "Upgrade".equalsIgnoreCase(key))
continue;
os.write('\r');
os.write('\n');
os.printLatin1NoLf(key);
os.write(':');
os.write(' ');
os.printLatin1NoLf((String) _headerValues.get(i));
if (debug) {
log.fine(_request.dbgId() + "" +
key + ": " + _headerValues.get(i));
}
}
long now = Alarm.getCurrentTime();
ArrayList<Cookie> cookiesOut = response.getCookies();
if (cookiesOut != null) {
for (int i = 0; i < cookiesOut.size(); i++) {
Cookie cookie = cookiesOut.get(i);
int cookieVersion = cookie.getVersion();
CharBuffer cb = _cb;
// XXX:
fillCookie(cb, cookie, now, cookieVersion, false);
os.printLatin1("\r\nSet-Cookie: ");
os.printLatin1(cb.getBuffer(), 0, cb.getLength());
if (cookieVersion > 0) {
fillCookie(cb, cookie, now, cookieVersion, true);
os.printLatin1("\r\nSet-Cookie2: ");
os.printLatin1(cb.getBuffer(), 0, cb.getLength());
}
if (debug)
log.fine(_request.dbgId() + "Set-Cookie: " + cb);
}
}
if (contentType != null) {
if (charEncoding == null) {
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);
os.write(_charsetBytes, 0, _charsetBytes.length);
os.printLatin1(charEncoding);
if (debug) {
log.fine(_request.dbgId() + "Content-Type: " + contentType
+ "; charset=" + charEncoding);
}
}
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(_request.dbgId() + "Content-Length: " + _contentLength);
}
else if (statusCode == HttpServletResponse.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 == HttpServletResponse.SC_NO_CONTENT) {
hasContentLength = true;
os.write(_contentLengthBytes, 0, _contentLengthBytes.length);
os.print(0);
setHead();
if (debug)
log.fine(_request.dbgId() + "Content-Length: 0");
}
else if (length >= 0) {
os.write(_contentLengthBytes, 0, _contentLengthBytes.length);
os.print(length);
hasContentLength = true;
if (debug)
log.fine(_request.dbgId() + "Content-Length: " + length);
}
if (version < HttpRequest.HTTP_1_1) {
_request.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 (_request.isKeepalive()) {
}
else if (isUpgrade) {
_request.killKeepalive("http response upgrade");
}
else {
os.write(_connectionCloseBytes, 0, _connectionCloseBytes.length);
if (debug)
log.fine(_request.dbgId() + "Connection: close");
}
}
if (HttpRequest.HTTP_1_1 <= version
&& ! hasContentLength
&& ! isHead) {
os.printLatin1("\r\nTransfer-Encoding: chunked");
_isChunked = true;
if (debug)
log.fine(_request.dbgId() + "Transfer-Encoding: chunked");
}
if (_lastDate / 1000 != now / 1000) {
fillDate(now);
}
if (_isChunked)
os.write(_dateBuffer, 0, _dateBufferLength - 2);
else
os.write(_dateBuffer, 0, _dateBufferLength);
return _isChunked;
}