Optionally compress large responses, if the browser accepts deflate compression. This mainly improves start up time.
git-svn-id: https://shellinabox.googlecode.com/svn/trunk@142 0da03de8-d603-11dd-86c2-0f8696b7b6f9
This commit is contained in:
parent
ca18a5346f
commit
8920606f6f
9 changed files with 2764 additions and 2148 deletions
|
@ -1,3 +1,8 @@
|
||||||
|
2009-07-08 Markus Gutschke <markus@shellinabox.com>
|
||||||
|
|
||||||
|
* Optionally compress large responses, if the browser accepts
|
||||||
|
deflate compression. This mainly improves start up time.
|
||||||
|
|
||||||
2009-07-06 Markus Gutschke <markus@shellinabox.com>
|
2009-07-06 Markus Gutschke <markus@shellinabox.com>
|
||||||
|
|
||||||
* Making it easier to host the terminal on non-root URLs by always
|
* Making it easier to host the terminal on non-root URLs by always
|
||||||
|
|
5
config.h
5
config.h
|
@ -106,6 +106,9 @@
|
||||||
/* Define to 1 if you have the <utmp.h> header file. */
|
/* Define to 1 if you have the <utmp.h> header file. */
|
||||||
#define HAVE_UTMP_H 1
|
#define HAVE_UTMP_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if zlib development files are installed */
|
||||||
|
#define HAVE_ZLIB 1
|
||||||
|
|
||||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||||
*/
|
*/
|
||||||
#define LT_OBJDIR ".libs/"
|
#define LT_OBJDIR ".libs/"
|
||||||
|
@ -132,7 +135,7 @@
|
||||||
#define STDC_HEADERS 1
|
#define STDC_HEADERS 1
|
||||||
|
|
||||||
/* Most recent revision number in the version control system */
|
/* Most recent revision number in the version control system */
|
||||||
#define VCS_REVISION "141"
|
#define VCS_REVISION "142"
|
||||||
|
|
||||||
/* Version number of package */
|
/* Version number of package */
|
||||||
#define VERSION "2.9"
|
#define VERSION "2.9"
|
||||||
|
|
|
@ -105,6 +105,9 @@
|
||||||
/* Define to 1 if you have the <utmp.h> header file. */
|
/* Define to 1 if you have the <utmp.h> header file. */
|
||||||
#undef HAVE_UTMP_H
|
#undef HAVE_UTMP_H
|
||||||
|
|
||||||
|
/* Define to 1 if zlib development files are installed */
|
||||||
|
#undef HAVE_ZLIB
|
||||||
|
|
||||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||||
*/
|
*/
|
||||||
#undef LT_OBJDIR
|
#undef LT_OBJDIR
|
||||||
|
|
|
@ -2,7 +2,7 @@ AC_PREREQ(2.57)
|
||||||
|
|
||||||
dnl This is the one location where the authoritative version number is stored
|
dnl This is the one location where the authoritative version number is stored
|
||||||
AC_INIT(shellinabox, 2.9, markus@shellinabox.com)
|
AC_INIT(shellinabox, 2.9, markus@shellinabox.com)
|
||||||
VCS_REVISION=141
|
VCS_REVISION=142
|
||||||
AC_SUBST(VCS_REVISION)
|
AC_SUBST(VCS_REVISION)
|
||||||
AC_DEFINE_UNQUOTED(VCS_REVISION, "${VCS_REVISION}",
|
AC_DEFINE_UNQUOTED(VCS_REVISION, "${VCS_REVISION}",
|
||||||
[Most recent revision number in the version control system])
|
[Most recent revision number in the version control system])
|
||||||
|
@ -122,6 +122,12 @@ if test "x$enable_runtime_loading" == xno; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
AC_CHECK_LIB(z, deflate, [
|
||||||
|
AC_CHECK_HEADER(zlib.h, [LIBS="-lz $LIBS"
|
||||||
|
AC_DEFINE(HAVE_ZLIB, 1,
|
||||||
|
Define to 1 if zlib development files are installed)
|
||||||
|
])])
|
||||||
|
|
||||||
dnl Generate output files
|
dnl Generate output files
|
||||||
AC_CONFIG_FILES([Makefile])
|
AC_CONFIG_FILES([Makefile])
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
|
|
|
@ -1693,7 +1693,7 @@ VT100.prototype.toggleBell = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
VT100.prototype.about = function() {
|
VT100.prototype.about = function() {
|
||||||
alert("VT100 Terminal Emulator " + "2.9 (revision 141)" +
|
alert("VT100 Terminal Emulator " + "2.9 (revision 142)" +
|
||||||
"\nCopyright 2008-2009 by Markus Gutschke\n" +
|
"\nCopyright 2008-2009 by Markus Gutschke\n" +
|
||||||
"For more information check http://shellinabox.com");
|
"For more information check http://shellinabox.com");
|
||||||
};
|
};
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#include <math.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -58,6 +59,10 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
#include <zlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_STRLCAT
|
#ifdef HAVE_STRLCAT
|
||||||
#define strncat(a,b,c) ({ char *_a = (a); strlcat(_a, (b), (c)+1); _a; })
|
#define strncat(a,b,c) ({ char *_a = (a); strlcat(_a, (b), (c)+1); _a; })
|
||||||
#endif
|
#endif
|
||||||
|
@ -414,15 +419,87 @@ void deleteHttpConnection(struct HttpConnection *http) {
|
||||||
free(http);
|
free(http);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
static int httpAcceptsEncoding(struct HttpConnection *http,
|
||||||
|
const char *encoding) {
|
||||||
|
int encodingLength = strlen(encoding);
|
||||||
|
const char *accepts = getFromHashMap(&http->header, "accept-encoding");
|
||||||
|
if (!accepts) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
double all = -1.0;
|
||||||
|
double match = -1.0;
|
||||||
|
while (*accepts) {
|
||||||
|
while (*accepts == ' ' || *accepts == '\t' ||
|
||||||
|
*accepts == '\r' || *accepts == '\n') {
|
||||||
|
accepts++;
|
||||||
|
}
|
||||||
|
const char *ptr = accepts;
|
||||||
|
while (*ptr && *ptr != ',' && *ptr != ';' &&
|
||||||
|
*ptr != ' ' && *ptr != '\t' &&
|
||||||
|
*ptr != '\r' && *ptr != '\n') {
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
int isAll = ptr - accepts == 1 && *accepts == '*';
|
||||||
|
int isMatch = ptr - accepts == encodingLength &&
|
||||||
|
!strncasecmp(accepts, encoding, encodingLength);
|
||||||
|
while (*ptr && *ptr != ';' && *ptr != ',') {
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
double val = 1.0;
|
||||||
|
if (*ptr == ';') {
|
||||||
|
ptr++;
|
||||||
|
while (*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n') {
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
if ((*ptr | 0x20) == 'q') {
|
||||||
|
ptr++;
|
||||||
|
while (*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n') {
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
if (*ptr == '=') {
|
||||||
|
val = strtod(ptr + 1, (char **)&ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isnan(val) || val == -HUGE_VAL || val < 0) {
|
||||||
|
val = 0;
|
||||||
|
} else if (val == HUGE_VAL || val > 1.0) {
|
||||||
|
val = 1.0;
|
||||||
|
}
|
||||||
|
if (isAll) {
|
||||||
|
all = val;
|
||||||
|
} else if (isMatch) {
|
||||||
|
match = val;
|
||||||
|
}
|
||||||
|
while (*ptr && *ptr != ',') {
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
while (*ptr == ',') {
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
accepts = ptr;
|
||||||
|
}
|
||||||
|
if (match >= 0.0) {
|
||||||
|
return match > 0.0;
|
||||||
|
} else {
|
||||||
|
return all > 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void httpTransfer(struct HttpConnection *http, char *msg, int len) {
|
void httpTransfer(struct HttpConnection *http, char *msg, int len) {
|
||||||
check(msg);
|
check(msg);
|
||||||
check(len >= 0);
|
check(len >= 0);
|
||||||
|
|
||||||
|
int compress = 0;
|
||||||
|
char *contentLength = NULL;
|
||||||
if (!http->totalWritten) {
|
if (!http->totalWritten) {
|
||||||
// Perform some basic sanity checks. This does not necessarily catch all
|
// Perform some basic sanity checks. This does not necessarily catch all
|
||||||
// possible problems, though.
|
// possible problems, though.
|
||||||
int l = len;
|
int l = len;
|
||||||
for (char *eol, *lastLine = NULL, *line = msg;
|
char *line = msg;
|
||||||
|
for (char *eol, *lastLine = NULL;
|
||||||
l > 0 && (eol = memchr(line, '\n', l)) != NULL; ) {
|
l > 0 && (eol = memchr(line, '\n', l)) != NULL; ) {
|
||||||
// All lines end in CR LF
|
// All lines end in CR LF
|
||||||
check(eol[-1] == '\r');
|
check(eol[-1] == '\r');
|
||||||
|
@ -432,9 +509,9 @@ void httpTransfer(struct HttpConnection *http, char *msg, int len) {
|
||||||
check(!memcmp(line, "HTTP/1.", 7));
|
check(!memcmp(line, "HTTP/1.", 7));
|
||||||
check(line[7] >= '0' && line[7] <= '9' &&
|
check(line[7] >= '0' && line[7] <= '9' &&
|
||||||
(line[8] == ' ' || line[8] == '\t'));
|
(line[8] == ' ' || line[8] == '\t'));
|
||||||
int i = eol - line - 9;
|
int i = eol - line - 9;
|
||||||
for (char *ptr = line + 9; i-- > 0; ) {
|
for (char *ptr = line + 9; i-- > 0; ) {
|
||||||
char ch = *ptr++;
|
char ch = *ptr++;
|
||||||
if (ch < '0' || ch > '9') {
|
if (ch < '0' || ch > '9') {
|
||||||
check(ptr > line + 10);
|
check(ptr > line + 10);
|
||||||
check(ch == ' ' || ch == '\t');
|
check(ch == ' ' || ch == '\t');
|
||||||
|
@ -443,49 +520,122 @@ void httpTransfer(struct HttpConnection *http, char *msg, int len) {
|
||||||
}
|
}
|
||||||
check(i > 1);
|
check(i > 1);
|
||||||
} else if (line + 1 == eol) {
|
} else if (line + 1 == eol) {
|
||||||
// Don't send any data with HEAD requests
|
// Found the end of the headers.
|
||||||
check(l == 2 || strcmp(http->method, "HEAD"));
|
|
||||||
|
// Check that we don't send any data with HEAD requests
|
||||||
|
int isHead = !strcmp(http->method, "HEAD");
|
||||||
|
check(l == 2 || !isHead);
|
||||||
|
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
// Compress replies that might exceed the size of a single IP packet
|
||||||
|
compress = !isHead &&
|
||||||
|
!http->isPartialReply &&
|
||||||
|
len > 1400 &&
|
||||||
|
httpAcceptsEncoding(http, "deflate");
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
// Header lines either contain a colon, or they are continuation
|
// Header lines either contain a colon, or they are continuation
|
||||||
// lines
|
// lines
|
||||||
if (*line != ' ' && *line != '\t') {
|
if (*line != ' ' && *line != '\t') {
|
||||||
check(memchr(line, ':', eol - line));
|
check(memchr(line, ':', eol - line));
|
||||||
|
|
||||||
|
// Remember where we saw the Content-Length header. We need to edit
|
||||||
|
// it, if we end up sending compressed data
|
||||||
|
if (!strncasecmp(line, "Content-Length:", 15)) {
|
||||||
|
contentLength = line;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastLine = line;
|
lastLine = line;
|
||||||
l -= eol - line + 1;
|
l -= eol - line + 1;
|
||||||
line = eol + 1;
|
line = eol + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compress) {
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
// Create a copy of msg, and edit existing Content-Length line
|
||||||
|
// out of header.
|
||||||
|
char *compressed, *ptr;
|
||||||
|
check(compressed = malloc(len + 100));
|
||||||
|
if (contentLength) {
|
||||||
|
memcpy(compressed, msg, contentLength - msg);
|
||||||
|
char *part2 = strchr(contentLength, '\n');
|
||||||
|
check(part2++);
|
||||||
|
memcpy(compressed + (contentLength - msg), part2, line - part2);
|
||||||
|
ptr = compressed + (contentLength - msg) +
|
||||||
|
(line - part2);
|
||||||
|
} else {
|
||||||
|
memcpy(compressed, msg, line - msg);
|
||||||
|
ptr = compressed + (line - msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new Content-Encoding and new Content-Length lines. Leave enough
|
||||||
|
// space to later edit the actual length back in.
|
||||||
|
memcpy(ptr,
|
||||||
|
"Content-Encoding: deflate\r\n"
|
||||||
|
"Content-Length: \r\n\r\n",
|
||||||
|
66);
|
||||||
|
contentLength = ptr + 42;
|
||||||
|
ptr += 66;
|
||||||
|
|
||||||
|
// Compress the message
|
||||||
|
z_stream strm = { .zalloc = Z_NULL,
|
||||||
|
.zfree = Z_NULL,
|
||||||
|
.opaque = Z_NULL,
|
||||||
|
.avail_in = l - 2,
|
||||||
|
.next_in = (unsigned char *)line + 2,
|
||||||
|
.avail_out = len - (ptr - compressed),
|
||||||
|
.next_out = (unsigned char *)ptr
|
||||||
|
};
|
||||||
|
if (deflateInit(&strm, Z_DEFAULT_COMPRESSION) == Z_OK) {
|
||||||
|
if (deflate(&strm, Z_FINISH) == Z_STREAM_END) {
|
||||||
|
// Compression was successful and resulted in reduction in size
|
||||||
|
debug("Compressed response from %d to %d", len, len-strm.avail_out);
|
||||||
|
free(msg);
|
||||||
|
msg = compressed;
|
||||||
|
len -= strm.avail_out;
|
||||||
|
snprintf(contentLength, 21, "%20d", len - (ptr - compressed));
|
||||||
|
contentLength[20] = '\r';
|
||||||
|
} else {
|
||||||
|
free(compressed);
|
||||||
|
}
|
||||||
|
deflateEnd(&strm);
|
||||||
|
} else {
|
||||||
|
free(compressed);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
http->totalWritten += len;
|
|
||||||
|
http->totalWritten += len;
|
||||||
if (!len) {
|
if (!len) {
|
||||||
free(msg);
|
free(msg);
|
||||||
} else if (http->msg) {
|
} else if (http->msg) {
|
||||||
check(http->msg = realloc(http->msg,
|
check(http->msg = realloc(http->msg,
|
||||||
http->msgLength - http->msgOffset + len));
|
http->msgLength - http->msgOffset + len));
|
||||||
if (http->msgOffset) {
|
if (http->msgOffset) {
|
||||||
memmove(http->msg, http->msg + http->msgOffset,
|
memmove(http->msg, http->msg + http->msgOffset,
|
||||||
http->msgLength - http->msgOffset);
|
http->msgLength - http->msgOffset);
|
||||||
http->msgLength -= http->msgOffset;
|
http->msgLength -= http->msgOffset;
|
||||||
http->msgOffset = 0;
|
http->msgOffset = 0;
|
||||||
}
|
}
|
||||||
memcpy(http->msg + http->msgLength, msg, len);
|
memcpy(http->msg + http->msgLength, msg, len);
|
||||||
http->msgLength += len;
|
http->msgLength += len;
|
||||||
free(msg);
|
free(msg);
|
||||||
} else {
|
} else {
|
||||||
check(!http->msgOffset);
|
check(!http->msgOffset);
|
||||||
http->msg = msg;
|
http->msg = msg;
|
||||||
http->msgLength = len;
|
http->msgLength = len;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internet Explorer prior to version 7 has a bug when send XMLHttpRequests
|
// Internet Explorer prior to version 7 has a bug when sending
|
||||||
// over HTTPS that go through a proxy. It won't see the reply until we
|
// XMLHttpRequests over HTTPS that go through a proxy. It won't see the
|
||||||
// close the connection.
|
// reply until we close the connection.
|
||||||
int ieBug = 0;
|
int ieBug = 0;
|
||||||
if (http->sslHndl) {
|
if (http->sslHndl) {
|
||||||
const char *userAgent = getFromHashMap(&http->header, "user-agent");
|
const char *userAgent = getFromHashMap(&http->header, "user-agent");
|
||||||
const char *msie = userAgent ? strstr(userAgent, "MSIE ") : NULL;
|
const char *msie = userAgent ? strstr(userAgent, "MSIE ") : NULL;
|
||||||
if (msie && msie[5] >= '4' && msie[5] <= '6') {
|
if (msie && msie[5] >= '4' && msie[5] <= '6') {
|
||||||
ieBug++;
|
ieBug++;
|
||||||
}
|
}
|
||||||
|
@ -499,20 +649,20 @@ void httpTransfer(struct HttpConnection *http, char *msg, int len) {
|
||||||
// return additional data in subsequent calls to the callback handler.
|
// return additional data in subsequent calls to the callback handler.
|
||||||
if (http->isSuspended || http->isPartialReply) {
|
if (http->isSuspended || http->isPartialReply) {
|
||||||
if (http->msg && http->msgLength > 0) {
|
if (http->msg && http->msgLength > 0) {
|
||||||
int wrote = httpWrite(http, http->msg, http->msgLength);
|
int wrote = httpWrite(http, http->msg, http->msgLength);
|
||||||
if (wrote < 0 && errno != EAGAIN) {
|
if (wrote < 0 && errno != EAGAIN) {
|
||||||
httpCloseRead(http);
|
httpCloseRead(http);
|
||||||
free(http->msg);
|
free(http->msg);
|
||||||
http->msgLength = 0;
|
http->msgLength = 0;
|
||||||
http->msg = NULL;
|
http->msg = NULL;
|
||||||
} else if (wrote > 0) {
|
} else if (wrote > 0) {
|
||||||
if (wrote == http->msgLength) {
|
if (wrote == http->msgLength) {
|
||||||
free(http->msg);
|
free(http->msg);
|
||||||
http->msgLength = 0;
|
http->msgLength = 0;
|
||||||
http->msg = NULL;
|
http->msg = NULL;
|
||||||
} else {
|
} else {
|
||||||
memmove(http->msg, http->msg + wrote, http->msgLength - wrote);
|
memmove(http->msg, http->msg + wrote, http->msgLength - wrote);
|
||||||
http->msgLength -= wrote;
|
http->msgLength -= wrote;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -355,7 +355,7 @@ ShellInABox.prototype.extendContextMenu = function(entries, actions) {
|
||||||
};
|
};
|
||||||
|
|
||||||
ShellInABox.prototype.about = function() {
|
ShellInABox.prototype.about = function() {
|
||||||
alert("Shell In A Box version " + "2.9 (revision 141)" +
|
alert("Shell In A Box version " + "2.9 (revision 142)" +
|
||||||
"\nCopyright 2008-2009 by Markus Gutschke\n" +
|
"\nCopyright 2008-2009 by Markus Gutschke\n" +
|
||||||
"For more information check http://shellinabox.com" +
|
"For more information check http://shellinabox.com" +
|
||||||
(typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ?
|
(typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ?
|
||||||
|
|
|
@ -1693,7 +1693,7 @@ VT100.prototype.toggleBell = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
VT100.prototype.about = function() {
|
VT100.prototype.about = function() {
|
||||||
alert("VT100 Terminal Emulator " + "2.9 (revision 141)" +
|
alert("VT100 Terminal Emulator " + "2.9 (revision 142)" +
|
||||||
"\nCopyright 2008-2009 by Markus Gutschke\n" +
|
"\nCopyright 2008-2009 by Markus Gutschke\n" +
|
||||||
"For more information check http://shellinabox.com");
|
"For more information check http://shellinabox.com");
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue