io.datakernel.http.HttpDate Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datakernel-http Show documentation
Show all versions of datakernel-http Show documentation
High-performance asynchronous HTTP clients and servers collection.
Package contains a bunch of different built-in servlets for request dispatching, loading of a static content, etc.
/*
* Copyright (C) 2015-2018 SoftIndex LLC.
*
* Licensed 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.
*/
package io.datakernel.http;
import io.datakernel.bytebuf.ByteBuf;
import io.datakernel.exception.ParseException;
import static io.datakernel.bytebuf.ByteBufStrings.encodeAscii;
import static io.datakernel.bytebuf.ByteBufStrings.encodeDecimal;
import static io.datakernel.http.HttpUtils.decodeUnsignedInt;
/* <[RFC2616], Section 3.3.1> case-sensitive
Can't parse dates earlier than 1970*/
final class HttpDate {
private static final int HOUR_SECONDS = (60 * 60);
private static final int DAY_SECONDS = 24 * HOUR_SECONDS;
private static final int YEAR_SECONDS = 365 * DAY_SECONDS;
private static final int FOUR_YEAR_SECONDS = (1461 * DAY_SECONDS);
private static final int[] DAYS_IN_MONTH = new int[]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
private static final int[] DAYS_IN_MONTH_LEAP = new int[]{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
private static final byte[] GMT = encodeAscii("GMT");
private static final byte[][] DAYS_OF_WEEK = new byte[7][];
static {
DAYS_OF_WEEK[0] = encodeAscii("Sun");
DAYS_OF_WEEK[1] = encodeAscii("Mon");
DAYS_OF_WEEK[2] = encodeAscii("Tue");
DAYS_OF_WEEK[3] = encodeAscii("Wed");
DAYS_OF_WEEK[4] = encodeAscii("Thu");
DAYS_OF_WEEK[5] = encodeAscii("Fri");
DAYS_OF_WEEK[6] = encodeAscii("Sat");
}
private static final byte[][] MONTHS_IN_YEAR = new byte[12][];
static {
MONTHS_IN_YEAR[0] = encodeAscii("Jan");
MONTHS_IN_YEAR[1] = encodeAscii("Feb");
MONTHS_IN_YEAR[2] = encodeAscii("Mar");
MONTHS_IN_YEAR[3] = encodeAscii("Apr");
MONTHS_IN_YEAR[4] = encodeAscii("May");
MONTHS_IN_YEAR[5] = encodeAscii("Jun");
MONTHS_IN_YEAR[6] = encodeAscii("Jul");
MONTHS_IN_YEAR[7] = encodeAscii("Aug");
MONTHS_IN_YEAR[8] = encodeAscii("Sep");
MONTHS_IN_YEAR[9] = encodeAscii("Oct");
MONTHS_IN_YEAR[10] = encodeAscii("Nov");
MONTHS_IN_YEAR[11] = encodeAscii("Dec");
}
private HttpDate() {}
static long parse(byte[] bytes, int start) throws ParseException {
try {
int day = decodeUnsignedInt(bytes, start + 5, 2);
int month = -1;
for (int i = 0; i < MONTHS_IN_YEAR.length; i++) {
byte[] entry = MONTHS_IN_YEAR[i];
if (entry[0] == bytes[start + 8] &&
entry[1] == bytes[start + 9] &&
entry[2] == bytes[start + 10]) {
month = i;
}
}
int yearLength = '0' <= bytes[start + 12 + 2] && bytes[start + 12 + 2] <= '9' ? 4 : 2;
int year = (yearLength == 2 ? 2000 : 0) + decodeUnsignedInt(bytes, start + 12, yearLength);
int hour = decodeUnsignedInt(bytes, start + 13 + yearLength, 2);
int minutes = decodeUnsignedInt(bytes, start + 16 + yearLength, 2);
int seconds = decodeUnsignedInt(bytes, start + 19 + yearLength, 2);
boolean isLeapYear = isLeap(year);
int[] days = isLeapYear ? DAYS_IN_MONTH_LEAP : DAYS_IN_MONTH;
year = year - 1970;
int yearsLeft = year % 4;
long timestamp = 0;
int fy = (year - yearsLeft) / 4;
for (int i = 0; i < fy; i++) {
timestamp += FOUR_YEAR_SECONDS;
}
timestamp += yearsLeft * YEAR_SECONDS;
if (yearsLeft > 2) {
// 1972 was a leap year and this code is assumed to be either deprecated or fixed before year 2100
timestamp += DAY_SECONDS;
}
for (int i = 0; i < month; i++) {
timestamp += (DAY_SECONDS * days[i]);
}
for (int i = 1; i < day; i++) {
timestamp += DAY_SECONDS;
}
timestamp += ((60 * hour + minutes) * 60) + seconds;
timestamp *= 1_000l;
return timestamp;
} catch (RuntimeException e) {
throw new ParseException();
}
}
static void render(long timestamp, ByteBuf buf) {
int pos = render(timestamp, buf.array(), buf.writePosition());
buf.writePosition(pos);
}
static int render(long timestamp, byte[] bytes, int pos) {
long secondsFrom1970 = timestamp / 1000L;
int fourYears = (int) (secondsFrom1970 / FOUR_YEAR_SECONDS);
int year = fourYears * 4 + 1970;
int seconds = (int) (secondsFrom1970 - fourYears * FOUR_YEAR_SECONDS);
boolean isLeapYear = false;
if (seconds >= YEAR_SECONDS) {
year++;
seconds -= YEAR_SECONDS;
if (seconds >= YEAR_SECONDS) {
year++;
seconds -= YEAR_SECONDS;
if (seconds >= (YEAR_SECONDS + DAY_SECONDS)) {
year++;
seconds -= (YEAR_SECONDS + DAY_SECONDS);
} else {
isLeapYear = true;
}
}
}
int dayOfYear = seconds / DAY_SECONDS;
seconds -= dayOfYear * DAY_SECONDS;
int[] monthsDays = isLeapYear ? DAYS_IN_MONTH_LEAP : DAYS_IN_MONTH;
int month = 0;
int day = dayOfYear;
while (day >= monthsDays[month]) {
day -= monthsDays[month++];
}
int hours = seconds / (60 * 60);
seconds -= hours * (60 * 60);
int minutes = seconds / 60;
seconds -= minutes * 60;
int dayOfWeek = (int) ((timestamp / (24 * 60 * 60 * 1000L) + 4) % 7) + 1;
byte[] stringDay = DAYS_OF_WEEK[dayOfWeek - 1];
System.arraycopy(stringDay, 0, bytes, pos, stringDay.length);
pos += stringDay.length;
bytes[pos++] = ',';
bytes[pos++] = ' ';
if (day < 9) {
bytes[pos++] = '0';
}
day += 1;
pos += encodeDecimal(bytes, pos, day);
bytes[pos++] = ' ';
byte[] stringMonth = MONTHS_IN_YEAR[month];
System.arraycopy(stringMonth, 0, bytes, pos, stringMonth.length);
pos += stringMonth.length;
bytes[pos++] = ' ';
pos += encodeDecimal(bytes, pos, year);
bytes[pos++] = ' ';
if (hours < 10) {
bytes[pos++] = '0';
}
pos += encodeDecimal(bytes, pos, hours);
bytes[pos++] = ':';
if (minutes < 10) {
bytes[pos++] = '0';
}
pos += encodeDecimal(bytes, pos, minutes);
bytes[pos++] = ':';
if (seconds < 10) {
bytes[pos++] = '0';
}
pos += encodeDecimal(bytes, pos, seconds);
bytes[pos++] = ' ';
System.arraycopy(GMT, 0, bytes, pos, GMT.length);
return pos + 3;
}
private static boolean isLeap(int year) {
if (year % 4 == 0) {
if (year % 100 == 0) {
if (year % 400 == 0) {
return true;
}
} else {
return true;
}
}
return false;
}
}