nl7chttp.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
/*
* HTTP connection persistent headers, mblk_t's, and state values stored in
* (struct sonode *).so_nl7c_flags & NL7C_SCHEMEPRIV.
*/
char Shttp_conn_cl[] = "Connection: close\r\n";
char Shttp_conn_ka[] = "Connection: Keep-Alive\r\n";
#define HTTP_CONN_CL 0x00010000
#define HTTP_CONN_KA 0x00020000
/*
* HTTP scheme private state:
*/
typedef struct http_s {
} http_t;
static kmem_cache_t *http_kmc;
/*
* HTTP parser action values:
*/
typedef enum act_e {
REQUEST = 0x0001,
NUMERIC = 0x0002,
QUALIFIER = 0x0004,
PASS = 0x0008,
FILTER = 0x0010,
NOCACHE = 0x0020,
HASH = 0x0040,
DATE = 0x0080,
ETAG = 0x0100,
RESPONSE = 0x0200,
URIABS = 0x0400,
URIREL = 0x0800
} act_t;
/*
* HTTP parser token:
*/
typedef struct token_s {
int tokid; /* Token ident */
char *text; /* Token text */
} token_t;
/*
* The ttree_t (or token tree) is an ascending ordered binary tree
* built by ttree_build() from an array of tokens and subsequently
* used by ttree_line_parse() to parse multiline text data.
*/
typedef struct ttree_s {
} ttree_t;
/*
* Note: req_tree[] and res_tree[] must be in ascending case insensitive
* order of the char[] strings used to initialize each element.
*
* See "nl7ctokreq.txt" and "nl7ctokres.txt" which are processed by
* "nl7ctokgen" to produce "nl7ctokgen.h" and included here.
*/
#define INIT(s, t) {s, S##s, t}
#include "nl7ctokgen.h"
/*
* HTTP date routines:
*/
"friday", "saturday", 0};
"Aug", "Sep", "Oct", "Nov", "Dec", 0};
/*
* http_date2time_t(const char *) - returns the time(2) value (i.e.
* the value 0 is Thu, 01 Jan 1970 00:00:00 GMT) for the following
* time formats used by HTTP request and response headers:
*
* 1) Sun, 07 Dec 1998 14:49:37 GMT ; RFC 822, updated by RFC 1123
* 2) Sunday, 07-Dec-98 14:49:37 GMT ; RFC 850, obsoleted by RFC 1036
* 3) Sun Nov 7 14:49:37 1998 ; ANSI C's asctime() format
* 4) 60 ; Time delta of N seconds
*
* On error a time_t value of -1 is returned.
*
* All dates are GMT (must be part of the date string for types
* 1 and 2 and not for type 1).
*
* Note, the given mstr_t pointed to by *sp will be modified.
*/
static time_t
{
char **tpp;
char *tp;
char c, sc;
ssize_t n;
/* Parse and skip day-of-week (we don't use it) */
n = 0;
c = *cp++;
if (c == ',' || c == ' ')
break;
c = tolower(c);
break;
continue;
}
tp++;
}
/* Not case 1-3, try 4 */
c = *cp;
if (isdigit(c)) {
cp++;
n *= 10;
n += c - '0';
continue;
}
/* An invalid date sytax */
return (-1);
}
/* Case 4, delta from current time */
return (gethrestime_sec() + n);
}
if (c == ',') {
/* Case 1 or 2, skip <SP> */
return (-1);
c = *cp++;
if (c != ' ')
return (-1);
/* Get day of the month */
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n = c - '0';
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n *= 10;
n += c - '0';
day = n;
return (-1);
return (-1);
/* Parse month */
n = 0;
c = *cp;
if (c == sc) {
cp++;
break;
}
c = tolower(c);
break;
n++;
continue;
}
cp++;
tp++;
}
return (-1);
month = n;
/* Get year */
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n = c - '0';
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n *= 10;
n += c - '0';
return (-1);
c = *cp++;
if (sc == ' ') {
/* Case 1, get 2 more year digits */
if (! isdigit(c))
return (-1);
n *= 10;
n += c - '0';
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n *= 10;
n += c - '0';
/* Get seperator char */
return (-1);
c = *cp;
if (c != ' ')
return (-1);
cp++;
} else {
/*
* Case 2, 2 digit year and as this is a so-called
* Unix date format and the begining of time was
* 1970 so we can extend this obsoleted date syntax
* past the year 1999 into the year 2038 for 32 bit
* machines and through 2069 for 64 bit machines.
*/
if (n > 69)
n += 1900;
else
n += 2000;
}
year = n;
/* Get GMT time */
if (c != ' ')
return (-1);
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n = c - '0';
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n *= 10;
n += c - '0';
hour = n;
return (-1);
c = *cp++;
if (c != ':')
return (-1);
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n = c - '0';
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n *= 10;
n += c - '0';
min = n;
return (-1);
c = *cp++;
if (c != ':')
return (-1);
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n = c - '0';
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n *= 10;
n += c - '0';
sec = n;
return (-1);
c = *cp++;
if (c != ' ')
return (-1);
return (-1);
c = *cp++;
if (c != 'G')
return (-1);
return (-1);
c = *cp++;
if (c != 'M')
return (-1);
return (-1);
c = *cp++;
if (c != 'T')
return (-1);
} else {
/* case 3, parse month */
sc = c;
n = 0;
c = *cp;
if (c == sc) {
cp++;
break;
}
c = tolower(c);
break;
n++;
continue;
}
cp++;
tp++;
}
return (-1);
month = n;
/* Get day of the month */
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n = c - '0';
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n *= 10;
n += c - '0';
day = n;
/* Skip <SP> */
return (-1);
c = *cp++;
if (c != ' ')
return (-1);
/* Get time */
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n = c - '0';
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n *= 10;
n += c - '0';
hour = n;
return (-1);
c = *cp++;
if (c != ':')
return (-1);
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n = c - '0';
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n *= 10;
n += c - '0';
min = n;
return (-1);
c = *cp++;
if (c != ':')
return (-1);
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n = c - '0';
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n *= 10;
n += c - '0';
sec = n;
/* Skip <SP> */
return (-1);
c = *cp++;
if (c != ' ')
return (-1);
/* Get year */
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n = c - '0';
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n *= 10;
n += c - '0';
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n *= 10;
n += c - '0';
return (-1);
c = *cp++;
if (! isdigit(c))
return (-1);
n *= 10;
n += c - '0';
year = n;
}
/* Last, caclulate seconds since Unix day zero */
if (month < 2)
leap--;
return (secs);
}
/*
* Given the ttree_t pointer "*t", parse the char buffer pointed to
* by "**cpp" of multiline text data up to the pointer "**epp", the
* pointer "*hash" points to the current text hash.
*
* If a match is found a pointer to the ttree_t token will be returned,
* "**cpp" will point to the next line, "**epp" will point to the first
* EOL char, "**hpp" will point to remainder of the parse data (if none,
* **hpp == **epp), and "*hash" will be updated.
*
* If no match, as above except "**hpp" points to the begining of the
* line and "*hash" wont be updated.
*
* If no EOL is found NULL is returned, "**epp" is set to NULL, no further
* calls can be made until additional data is ready and all arguments are
* reset.
*
* If EOH (i.e. an empty line) NULL is returned, "**hpp" is set to NULL,
* *cpp points to past EOH, no further calls can be made.
*/
static token_t *
{
int parse; /* parse state */
/* Special case, check for EOH (i.e. empty line) */
if (ca == '\n') {
/* End of header */
return (NULL);
} else if (ca == '\r') {
cp++;
if (ca == '\n') {
/* End of header */
return (NULL);
}
}
}
}
/* Get next parse text char */
if (cb != 0) {
/* Get next current line char */
/* EOL, always go less than */
t = t->lt;
} else {
/* Case insensitive */
/* Char match, next char */
tp++;
continue;
}
/* Go less than */
t = t->lt;
} else {
/* Go greater than */
t = t->gt;
}
}
/* Null node, so descend to < node */
t = t->lt;
}
if (t != NULL) {
/* Initialize for next node compare */
continue;
}
/*
* End of tree walk, no match, return pointer
* to the start of line then below find EOL.
*/
} else {
/*
* End of token text, match, return pointer to
* the rest of header text then below find EOL.
*/
}
/*
* Find end of line. Note, the HTTP line syntax supports
* implicit multi-line if the next line starts with a <SP>
* or <HT>.
*/
parse = 0;
parse = 1;
parse = 2;
parse = 2;
parse++;
} else if (parse > 2) {
parse = 0;
} else if (parse == 2) {
break;
hv &= 0xFFFFFF;
}
cp++;
}
if (parse < 2) {
/* No EOL, not enough data */
}
/*
* Return updated hash value (if any), update parse current
* pointer for next call (i.e. begin of next line), and last
* return pointer to the matching token_t.
*/
}
/*
* End of parse text, ...
*/
return (NULL);
}
/*
* Given a NULL terminated array of token_t(s) ordered in ascending
* case insensitive order a binary tree is allocated and populated with
* pointers into the array and a pointer to the root node is returned.
*
* Todo, for maximum ttree parse efficiency needs to be path compressed,
* the function ttree_line_parse() handles the empty nodes correctly.
*/
static ttree_t *
{
/* calc the size of the tree */
;
/* allocate the tree */
/* walk the tree and populate from list vector */
while (lvl >>= 1) {
} else {
}
if (inc) {
} else {
}
}
}
}
void
nl7c_http_init(void)
{
int n;
n = sizeof (Shttp_conn_cl) - 1;
http_conn_cl->b_wptr += n;
n = sizeof (Shttp_conn_ka) - 1;
http_conn_ka->b_wptr += n;
}
void
nl7c_http_free(void *arg)
{
}
#define STR_T_NOTCMP_OPT(a, b, m) ( \
#define STR_T_NOTCMP(a, b, m) ( \
STR_T_NOTCMP_OPT(a, b, m))
{
return (B_FALSE);
return (B_TRUE);
}
/*
* Return the appropriate HTTP connection persist header
* based on the request HTTP persistent header state.
*/
mblk_t *
{
if (flags & HTTP_CONN_CL)
else if (flags & HTTP_CONN_KA)
else
return (mp);
}
/*
* Parse the buffer *p of size len and update the uri_desc_t *uri and our
* http_t *http with the results.
*/
{
char *hp;
char *sep;
unsigned hash = 0;
char *HTTP = "HTTP/";
goto pass;
}
/*
*/
if (*cp == '\r') {
/*
* Special case for a Request-Line without an HTTP version,
* assume it's an old style, i.e. HTTP version 0.9 request.
*/
goto got_version;
}
/*
* Skip URI path delimiter, must be a <SP>.
*/
if (*cp++ != ' ')
/* Unkown or bad Request-Line format, just punt */
goto pass;
/*
* The URI parser has parsed through the URI and the <SP>
*/
HTTP++;
cp++;
}
if (*HTTP != 0) {
goto more;
goto pass;
}
goto more;
goto pass;
goto more;
if (*cp++ != '.')
goto pass;
goto more;
goto pass;
goto more;
if (*cp++ != '\r')
goto pass;
goto more;
if (*cp++ != '\n')
goto pass;
/*
* Initialize persistent state based on HTTP version.
*/
/* 1.1 persistent by default */
} else {
/* 1.0 isn't persistent by default */
}
/* Before 1.0 no persistent connections */
} else {
/* >= 2.0 not supported (yet) */
goto pass;
}
/*
* Parse HTTP headers through the EOH
* (End Of Header, i.e. an empty line).
*/
/* Get the next line */
/*
* Header field text is used to qualify this
* optionally convert and store *http.
*/
char c;
int n = 0;
c = *hp++;
if (! isdigit(c))
goto pass;
n *= 10;
n += c - '0';
}
}
case Qhdr_Accept_Charset:
break;
case Qhdr_Accept_Encoding:
break;
case Qhdr_Accept_Language:
break;
case Qhdr_Accept:
break;
case Qhdr_Authorization:
goto pass;
case Qhdr_Connection_close:
break;
break;
case Qhdr_Date:
break;
case Qhdr_ETag:
break;
case Qhdr_Host:
break;
case Qhdr_If_Modified_Since:
break;
case Qhdr_If_Unmodified_Since:
break;
case Qhdr_Keep_Alive:
break;
case Qhdr_User_Agent:
break;
default:
break;
};
}
}
goto done;
goto more;
}
}
/* No EOH found */
goto more;
done:
/*
* Initialize socket persist state and response persist type
* flag based on the persist state of the request headers.
*
*/
if (persist)
else
if (! persist)
} else {
if (persist)
else
}
}
/*
* Last, update parse consumed text pointer.
*/
return (B_TRUE);
pass:
more:
return (B_FALSE);
}
{
char *hp;
unsigned hash = 0;
char *HTTP = "HTTP/";
int status = 0;
#ifdef NOT_YET
#endif
return (B_TRUE);
/*
* for the actual response major nor minor values as only the
* request values are used.
*/
HTTP++;
cp++;
}
if (*HTTP != 0) {
goto more;
goto pass;
}
goto more;
goto pass;
#ifdef NOT_YET
#else
cp++;
#endif
goto more;
if (*cp++ != '.')
goto pass;
goto more;
goto pass;
#ifdef NOT_YET
#else
cp++;
#endif
goto more;
/*
* Get the response code, if not 200 then pass on this response.
*/
if (*cp++ != ' ')
goto pass;
goto more;
do {
if (*cp == ' ')
break;
goto pass;
if (status)
status *= 10;
if (status != 200)
goto pass;
/*
* Initialize persistent state based on request HTTP version.
*/
/* 1.1 persistent by default */
} else {
/* 1.0 isn't persistent by default */
}
/* Before 1.0 no persistent connections */
} else {
/* >= 2.0 not supported (yet) */
goto pass;
}
/*
* Parse HTTP headers through the EOH
* (End Of Header, i.e. an empty line).
*/
/* Get the next line */
/*
* Header field text is used to qualify this
* optionally convert and store *http.
*/
char c;
int n = 0;
c = *hp++;
if (! isdigit(c))
goto pass;
n *= 10;
n += c - '0';
}
}
break;
break;
break;
case Shdr_Connection_close:
break;
break;
case Shdr_Content_Length:
break;
case Shdr_Date:
break;
case Shdr_ETag:
break;
case Shdr_Expires:
break;
case Shdr_Keep_Alive:
break;
case Shdr_Last_Modified:
break;
case Shdr_Set_Cookies:
default:
break;
};
}
/*
* Filter header, do a copyover the header
* text, guarenteed to be at least 1 byte.
*/
char filter[] = "NL7C-Filtered";
if (n > 0)
cop += n;
*cop++ = ':';
*cop++ = ' ';
}
}
goto done;
goto more;
}
}
/* No EOH found */
goto more;
done:
if (nocache) {
goto pass;
}
goto pass;
/* Save the HTTP header length and add to URI response length */
/* Set socket persist state */
if (persist)
else
/* No cache */
goto pass;
}
/* Have a valid expire and date so calc an lbolt expire */
} else if (nl7c_uri_ttl != -1) {
/* No valid expire speced and we have a TTL */
}
return (B_TRUE);
pass:
more:
return (B_FALSE);
}
{
int sz;
*(*wp)++ = 0;
sz++;
}
} else {
}
} else {
}
return (B_FALSE);
full:
return (B_TRUE);
}