michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.background.fxa; michael@0: michael@0: import java.net.URI; michael@0: import java.net.URISyntaxException; michael@0: import java.util.HashMap; michael@0: michael@0: import org.mozilla.gecko.background.common.log.Logger; michael@0: import org.mozilla.gecko.sync.net.Resource; michael@0: michael@0: import ch.boye.httpclientandroidlib.Header; michael@0: import ch.boye.httpclientandroidlib.HttpHeaders; michael@0: import ch.boye.httpclientandroidlib.HttpResponse; michael@0: import ch.boye.httpclientandroidlib.impl.cookie.DateParseException; michael@0: import ch.boye.httpclientandroidlib.impl.cookie.DateUtils; michael@0: michael@0: public class SkewHandler { michael@0: private static final String LOG_TAG = "SkewHandler"; michael@0: protected volatile long skewMillis = 0L; michael@0: protected final String hostname; michael@0: michael@0: private static final HashMap skewHandlers = new HashMap(); michael@0: michael@0: public static SkewHandler getSkewHandlerForResource(final Resource resource) { michael@0: return getSkewHandlerForHostname(resource.getHostname()); michael@0: } michael@0: michael@0: public static SkewHandler getSkewHandlerFromEndpointString(final String url) throws URISyntaxException { michael@0: if (url == null) { michael@0: throw new IllegalArgumentException("url must not be null."); michael@0: } michael@0: URI u = new URI(url); michael@0: return getSkewHandlerForHostname(u.getHost()); michael@0: } michael@0: michael@0: public static synchronized SkewHandler getSkewHandlerForHostname(final String hostname) { michael@0: SkewHandler handler = skewHandlers.get(hostname); michael@0: if (handler == null) { michael@0: handler = new SkewHandler(hostname); michael@0: skewHandlers.put(hostname, handler); michael@0: } michael@0: return handler; michael@0: } michael@0: michael@0: public static synchronized void clearSkewHandlers() { michael@0: skewHandlers.clear(); michael@0: } michael@0: michael@0: public SkewHandler(final String hostname) { michael@0: this.hostname = hostname; michael@0: } michael@0: michael@0: public boolean updateSkewFromServerMillis(long millis, long now) { michael@0: skewMillis = millis - now; michael@0: Logger.debug(LOG_TAG, "Updated skew: " + skewMillis + "ms for hostname " + this.hostname); michael@0: return true; michael@0: } michael@0: michael@0: public boolean updateSkewFromHTTPDateString(String date, long now) { michael@0: try { michael@0: final long millis = DateUtils.parseDate(date).getTime(); michael@0: return updateSkewFromServerMillis(millis, now); michael@0: } catch (DateParseException e) { michael@0: Logger.warn(LOG_TAG, "Unexpected: invalid Date header from " + this.hostname); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: public boolean updateSkewFromDateHeader(Header header, long now) { michael@0: String date = header.getValue(); michael@0: if (null == date) { michael@0: Logger.warn(LOG_TAG, "Unexpected: null Date header from " + this.hostname); michael@0: return false; michael@0: } michael@0: return updateSkewFromHTTPDateString(date, now); michael@0: } michael@0: michael@0: /** michael@0: * Update our tracked skew value to account for the local clock differing from michael@0: * the server's. michael@0: * michael@0: * @param response michael@0: * the received HTTP response. michael@0: * @param now michael@0: * the current time in milliseconds. michael@0: * @return true if the skew value was updated, false otherwise. michael@0: */ michael@0: public boolean updateSkew(HttpResponse response, long now) { michael@0: Header header = response.getFirstHeader(HttpHeaders.DATE); michael@0: if (null == header) { michael@0: Logger.warn(LOG_TAG, "Unexpected: missing Date header from " + this.hostname); michael@0: return false; michael@0: } michael@0: return updateSkewFromDateHeader(header, now); michael@0: } michael@0: michael@0: public long getSkewInMillis() { michael@0: return skewMillis; michael@0: } michael@0: michael@0: public long getSkewInSeconds() { michael@0: return skewMillis / 1000; michael@0: } michael@0: michael@0: public void resetSkew() { michael@0: skewMillis = 0L; michael@0: } michael@0: }