/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package com.apigee.sdk.apm.http.impl.client.cache;
import java.util.Date;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.protocol.HTTP;
import com.apigee.sdk.apm.http.annotation.Immutable;
import com.apigee.sdk.apm.http.client.cache.HeaderConstants;
import com.apigee.sdk.apm.http.client.cache.HttpCacheEntry;
/**
* Rebuilds an {@link HttpResponse} from a {@link CacheEntry}
*
* @since 4.1
*/
@Immutable
class CachedHttpResponseGenerator {
private final CacheValidityPolicy validityStrategy;
CachedHttpResponseGenerator(final CacheValidityPolicy validityStrategy) {
super();
this.validityStrategy = validityStrategy;
}
CachedHttpResponseGenerator() {
this(new CacheValidityPolicy());
}
/**
* If I was able to use a {@link CacheEntry} to response to the
* {@link org.apache.http.HttpRequest} then generate an {@link HttpResponse}
* based on the cache entry.
*
* @param entry
* {@link CacheEntry} to transform into an {@link HttpResponse}
* @return {@link HttpResponse} that was constructed
*/
HttpResponse generateResponse(HttpCacheEntry entry) {
Date now = new Date();
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
entry.getStatusCode(), entry.getReasonPhrase());
HttpEntity entity = new CacheEntity(entry);
response.setHeaders(entry.getAllHeaders());
addMissingContentLengthHeader(response, entity);
response.setEntity(entity);
long age = this.validityStrategy.getCurrentAgeSecs(entry, now);
if (age > 0) {
if (age >= Integer.MAX_VALUE) {
response.setHeader(HeaderConstants.AGE, "2147483648");
} else {
response.setHeader(HeaderConstants.AGE, "" + ((int) age));
}
}
return response;
}
/**
* Generate a 304 - Not Modified response from a {@link CacheEntry}. This
* should be used to respond to conditional requests, when the entry exists
* or has been revalidated.
*
* @param entry
* @return
*/
HttpResponse generateNotModifiedResponse(HttpCacheEntry entry) {
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_NOT_MODIFIED, "Not Modified");
// The response MUST include the following headers
// (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)
// - Date, unless its omission is required by section 14.8.1
Header dateHeader = entry.getFirstHeader("Date");
if (dateHeader == null) {
dateHeader = new BasicHeader("Date",
DateUtils.formatDate(new Date()));
}
response.addHeader(dateHeader);
// - ETag and/or Content-Location, if the header would have been sent
// in a 200 response to the same request
Header etagHeader = entry.getFirstHeader("ETag");
if (etagHeader != null) {
response.addHeader(etagHeader);
}
Header contentLocationHeader = entry.getFirstHeader("Content-Location");
if (contentLocationHeader != null) {
response.addHeader(contentLocationHeader);
}
// - Expires, Cache-Control, and/or Vary, if the field-value might
// differ from that sent in any previous response for the same
// variant
Header expiresHeader = entry.getFirstHeader("Expires");
if (expiresHeader != null) {
response.addHeader(expiresHeader);
}
Header cacheControlHeader = entry.getFirstHeader("Cache-Control");
if (cacheControlHeader != null) {
response.addHeader(cacheControlHeader);
}
Header varyHeader = entry.getFirstHeader("Vary");
if (varyHeader != null) {
response.addHeader(varyHeader);
}
return response;
}
private void addMissingContentLengthHeader(HttpResponse response,
HttpEntity entity) {
if (transferEncodingIsPresent(response))
return;
Header contentLength = response.getFirstHeader(HTTP.CONTENT_LEN);
if (contentLength == null) {
contentLength = new BasicHeader(HTTP.CONTENT_LEN,
Long.toString(entity.getContentLength()));
response.setHeader(contentLength);
}
}
private boolean transferEncodingIsPresent(HttpResponse response) {
Header hdr = response.getFirstHeader(HTTP.TRANSFER_ENCODING);
return hdr != null;
}
}