2008-02-03 05:27:18 +00:00
|
|
|
/*
|
2011-07-25 08:24:13 +00:00
|
|
|
* $Id$
|
2008-06-04 03:00:14 +00:00
|
|
|
*
|
2019-06-06 05:28:23 +00:00
|
|
|
Copyright (c) 2006-2019 Chung, Hyung-Hwan. All rights reserved.
|
2014-11-19 14:42:24 +00:00
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in the
|
|
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
|
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2008-02-03 05:27:18 +00:00
|
|
|
*/
|
|
|
|
|
2013-02-18 13:45:50 +00:00
|
|
|
#include <qse/http/htrd.h>
|
2009-05-08 07:15:04 +00:00
|
|
|
#include <qse/cmn/chr.h>
|
2012-09-14 16:18:35 +00:00
|
|
|
#include <qse/cmn/path.h>
|
2016-04-29 03:55:42 +00:00
|
|
|
#include "../cmn/mem-prv.h"
|
2008-02-03 05:27:18 +00:00
|
|
|
|
2011-07-28 08:35:28 +00:00
|
|
|
static const qse_mchar_t NUL = QSE_MT('\0');
|
2010-11-27 09:00:57 +00:00
|
|
|
|
2014-10-08 14:07:55 +00:00
|
|
|
/* for htrd->fed.s.flags */
|
2012-04-09 15:29:33 +00:00
|
|
|
#define CONSUME_UNTIL_CLOSE (1 << 0)
|
|
|
|
|
2014-10-08 14:07:55 +00:00
|
|
|
/* for htrd->flags */
|
|
|
|
#define FEEDING_SUSPENDED (1 << 0)
|
|
|
|
#define FEEDING_DUMMIFIED (1 << 1)
|
|
|
|
|
2011-07-28 08:35:28 +00:00
|
|
|
static QSE_INLINE int is_whspace_octet (qse_mchar_t c)
|
2010-10-22 07:29:12 +00:00
|
|
|
{
|
2011-07-28 08:35:28 +00:00
|
|
|
return c == QSE_MT(' ') || c == QSE_MT('\t') || c == QSE_MT('\r') || c == QSE_MT('\n');
|
2010-10-22 07:29:12 +00:00
|
|
|
}
|
|
|
|
|
2011-07-28 08:35:28 +00:00
|
|
|
static QSE_INLINE int is_space_octet (qse_mchar_t c)
|
2010-10-22 07:29:12 +00:00
|
|
|
{
|
2011-07-30 02:14:04 +00:00
|
|
|
return c == QSE_MT(' ') || c == QSE_MT('\t') || c == QSE_MT('\r');
|
2010-10-26 07:04:11 +00:00
|
|
|
}
|
|
|
|
|
2011-07-28 08:35:28 +00:00
|
|
|
static QSE_INLINE int is_purespace_octet (qse_mchar_t c)
|
2010-10-27 07:01:16 +00:00
|
|
|
{
|
2011-08-03 10:27:30 +00:00
|
|
|
return c == QSE_MT(' ') || c == QSE_MT('\t');
|
2010-10-27 07:01:16 +00:00
|
|
|
}
|
|
|
|
|
2011-07-28 08:35:28 +00:00
|
|
|
static QSE_INLINE int is_upalpha_octet (qse_mchar_t c)
|
2010-10-26 07:04:11 +00:00
|
|
|
{
|
2011-08-03 10:27:30 +00:00
|
|
|
return c >= QSE_MT('A') && c <= QSE_MT('Z');
|
2010-10-26 07:04:11 +00:00
|
|
|
}
|
|
|
|
|
2011-07-28 08:35:28 +00:00
|
|
|
static QSE_INLINE int is_loalpha_octet (qse_mchar_t c)
|
2010-10-26 07:04:11 +00:00
|
|
|
{
|
2011-08-03 10:27:30 +00:00
|
|
|
return c >= QSE_MT('a') && c <= QSE_MT('z');
|
2010-10-26 07:04:11 +00:00
|
|
|
}
|
|
|
|
|
2011-07-28 08:35:28 +00:00
|
|
|
static QSE_INLINE int is_alpha_octet (qse_mchar_t c)
|
2010-10-26 07:04:11 +00:00
|
|
|
{
|
2011-08-03 10:27:30 +00:00
|
|
|
return (c >= QSE_MT('A') && c <= QSE_MT('Z')) ||
|
|
|
|
(c >= QSE_MT('a') && c <= QSE_MT('z'));
|
2010-10-26 07:04:11 +00:00
|
|
|
}
|
|
|
|
|
2011-07-28 08:35:28 +00:00
|
|
|
static QSE_INLINE int is_digit_octet (qse_mchar_t c)
|
2010-10-26 07:04:11 +00:00
|
|
|
{
|
2011-08-03 10:27:30 +00:00
|
|
|
return c >= QSE_MT('0') && c <= QSE_MT('9');
|
2010-10-26 07:04:11 +00:00
|
|
|
}
|
|
|
|
|
2011-07-28 08:35:28 +00:00
|
|
|
static QSE_INLINE int is_xdigit_octet (qse_mchar_t c)
|
2010-10-26 07:04:11 +00:00
|
|
|
{
|
2011-08-03 10:27:30 +00:00
|
|
|
return (c >= QSE_MT('0') && c <= QSE_MT('9')) ||
|
|
|
|
(c >= QSE_MT('A') && c <= QSE_MT('F')) ||
|
|
|
|
(c >= QSE_MT('a') && c <= QSE_MT('f'));
|
2010-10-26 07:04:11 +00:00
|
|
|
}
|
|
|
|
|
2011-07-28 08:35:28 +00:00
|
|
|
static QSE_INLINE int digit_to_num (qse_mchar_t c)
|
2010-10-26 07:04:11 +00:00
|
|
|
{
|
2011-07-30 02:14:04 +00:00
|
|
|
if (c >= QSE_MT('0') && c <= QSE_MT('9')) return c - QSE_MT('0');
|
2010-10-26 07:04:11 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-07-28 08:35:28 +00:00
|
|
|
static QSE_INLINE int xdigit_to_num (qse_mchar_t c)
|
2010-10-26 07:04:11 +00:00
|
|
|
{
|
2011-11-09 00:54:27 +00:00
|
|
|
return QSE_MXDIGITTONUM (c);
|
2010-10-26 07:04:11 +00:00
|
|
|
}
|
|
|
|
|
2011-07-30 02:14:04 +00:00
|
|
|
|
2010-10-22 07:29:12 +00:00
|
|
|
static QSE_INLINE int push_to_buffer (
|
2011-07-22 09:50:38 +00:00
|
|
|
qse_htrd_t* htrd, qse_htob_t* octb,
|
2011-07-28 08:35:28 +00:00
|
|
|
const qse_mchar_t* ptr, qse_size_t len)
|
2010-10-22 07:29:12 +00:00
|
|
|
{
|
2011-06-30 09:12:36 +00:00
|
|
|
if (qse_mbs_ncat (octb, ptr, len) == (qse_size_t)-1)
|
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->errnum = QSE_HTRD_ENOMEM;
|
2011-06-30 09:12:36 +00:00
|
|
|
return -1;
|
2010-10-22 07:29:12 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-08 12:59:59 +00:00
|
|
|
static QSE_INLINE int push_content (
|
|
|
|
qse_htrd_t* htrd, const qse_mchar_t* ptr, qse_size_t len)
|
|
|
|
{
|
2012-04-13 15:21:36 +00:00
|
|
|
QSE_ASSERT (len > 0);
|
|
|
|
|
2012-02-08 12:59:59 +00:00
|
|
|
if (qse_htre_addcontent (&htrd->re, ptr, len) <= -1)
|
|
|
|
{
|
|
|
|
htrd->errnum = QSE_HTRD_ENOMEM;
|
|
|
|
return -1;
|
|
|
|
}
|
2012-02-09 12:54:00 +00:00
|
|
|
|
|
|
|
/* qse_htre_addcontent() returns 1 on full success and 0 if adding is
|
|
|
|
* skipped. i treat both as success */
|
2012-02-08 12:59:59 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
static QSE_INLINE void clear_feed (qse_htrd_t* htrd)
|
2010-11-11 08:28:38 +00:00
|
|
|
{
|
2011-06-23 10:17:35 +00:00
|
|
|
/* clear necessary part of the request/response before
|
|
|
|
* reading the next request/response */
|
2012-04-13 15:21:36 +00:00
|
|
|
htrd->clean = 1;
|
2011-07-22 09:50:38 +00:00
|
|
|
qse_htre_clear (&htrd->re);
|
2010-11-27 09:00:57 +00:00
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
qse_mbs_clear (&htrd->fed.b.tra);
|
|
|
|
qse_mbs_clear (&htrd->fed.b.raw);
|
2010-11-27 09:00:57 +00:00
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
QSE_MEMSET (&htrd->fed.s, 0, QSE_SIZEOF(htrd->fed.s));
|
2010-11-11 08:28:38 +00:00
|
|
|
}
|
|
|
|
|
2011-07-06 09:45:00 +00:00
|
|
|
qse_htrd_t* qse_htrd_open (qse_mmgr_t* mmgr, qse_size_t xtnsize)
|
2010-10-22 07:29:12 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
qse_htrd_t* htrd;
|
2010-10-22 07:29:12 +00:00
|
|
|
|
2014-07-11 14:17:00 +00:00
|
|
|
htrd = (qse_htrd_t*) QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(qse_htrd_t) + xtnsize);
|
|
|
|
if (htrd)
|
2010-10-22 07:29:12 +00:00
|
|
|
{
|
2014-07-11 14:17:00 +00:00
|
|
|
if (qse_htrd_init (htrd, mmgr) <= -1)
|
|
|
|
{
|
|
|
|
QSE_MMGR_FREE (mmgr, htrd);
|
|
|
|
return QSE_NULL;
|
|
|
|
}
|
|
|
|
else QSE_MEMSET (QSE_XTN(htrd), 0, xtnsize);
|
2010-10-22 07:29:12 +00:00
|
|
|
}
|
2011-07-22 09:50:38 +00:00
|
|
|
return htrd;
|
2010-10-22 07:29:12 +00:00
|
|
|
}
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
void qse_htrd_close (qse_htrd_t* htrd)
|
2010-10-22 07:29:12 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
qse_htrd_fini (htrd);
|
|
|
|
QSE_MMGR_FREE (htrd->mmgr, htrd);
|
2010-10-22 07:29:12 +00:00
|
|
|
}
|
|
|
|
|
2011-09-01 09:43:46 +00:00
|
|
|
int qse_htrd_init (qse_htrd_t* htrd, qse_mmgr_t* mmgr)
|
2010-10-22 07:29:12 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
QSE_MEMSET (htrd, 0, QSE_SIZEOF(*htrd));
|
|
|
|
htrd->mmgr = mmgr;
|
|
|
|
htrd->option = QSE_HTRD_REQUEST | QSE_HTRD_RESPONSE;
|
2010-10-22 07:29:12 +00:00
|
|
|
|
2011-07-29 09:32:56 +00:00
|
|
|
#if 0
|
2011-07-22 09:50:38 +00:00
|
|
|
qse_mbs_init (&htrd->tmp.qparam, htrd->mmgr, 0);
|
2011-07-29 09:32:56 +00:00
|
|
|
#endif
|
2011-07-22 09:50:38 +00:00
|
|
|
qse_mbs_init (&htrd->fed.b.raw, htrd->mmgr, 0);
|
|
|
|
qse_mbs_init (&htrd->fed.b.tra, htrd->mmgr, 0);
|
2010-10-27 07:01:16 +00:00
|
|
|
|
2011-09-01 09:43:46 +00:00
|
|
|
if (qse_htre_init (&htrd->re, mmgr) <= -1)
|
2010-10-27 07:01:16 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
qse_mbs_fini (&htrd->fed.b.tra);
|
|
|
|
qse_mbs_fini (&htrd->fed.b.raw);
|
2011-07-29 09:32:56 +00:00
|
|
|
#if 0
|
2011-07-22 09:50:38 +00:00
|
|
|
qse_mbs_fini (&htrd->tmp.qparam);
|
2011-07-29 09:32:56 +00:00
|
|
|
#endif
|
2011-09-01 09:43:46 +00:00
|
|
|
return -1;
|
2010-10-27 07:01:16 +00:00
|
|
|
}
|
|
|
|
|
2012-04-13 15:21:36 +00:00
|
|
|
htrd->clean = 1;
|
2011-09-01 09:43:46 +00:00
|
|
|
return 0;
|
2010-10-22 07:29:12 +00:00
|
|
|
}
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
void qse_htrd_fini (qse_htrd_t* htrd)
|
2010-10-22 07:29:12 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
qse_htre_fini (&htrd->re);
|
2011-06-30 09:12:36 +00:00
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
qse_mbs_fini (&htrd->fed.b.tra);
|
|
|
|
qse_mbs_fini (&htrd->fed.b.raw);
|
2011-07-29 09:32:56 +00:00
|
|
|
#if 0
|
2011-07-22 09:50:38 +00:00
|
|
|
qse_mbs_fini (&htrd->tmp.qparam);
|
2011-07-29 09:32:56 +00:00
|
|
|
#endif
|
2010-10-22 07:29:12 +00:00
|
|
|
}
|
|
|
|
|
2012-09-14 16:18:35 +00:00
|
|
|
static qse_mchar_t* parse_initial_line (qse_htrd_t* htrd, qse_mchar_t* line)
|
2010-10-22 07:29:12 +00:00
|
|
|
{
|
2011-07-28 08:35:28 +00:00
|
|
|
qse_mchar_t* p = line;
|
2014-07-08 14:30:42 +00:00
|
|
|
qse_mcstr_t tmp;
|
2010-10-22 07:29:12 +00:00
|
|
|
|
2010-10-26 07:04:11 +00:00
|
|
|
#if 0
|
|
|
|
/* ignore leading spaces excluding crlf */
|
|
|
|
while (is_space_octet(*p)) p++;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* the method should start with an alphabet */
|
2014-08-08 18:53:04 +00:00
|
|
|
if (!is_alpha_octet(*p)) goto badre;
|
2010-10-26 07:04:11 +00:00
|
|
|
|
|
|
|
/* get the method name */
|
2011-07-06 09:45:00 +00:00
|
|
|
tmp.ptr = p;
|
2014-08-08 18:53:04 +00:00
|
|
|
do { p++; } while (is_alpha_octet(*p));
|
2011-07-06 09:45:00 +00:00
|
|
|
tmp.len = p - tmp.ptr;
|
2010-10-26 07:04:11 +00:00
|
|
|
|
2012-03-30 06:12:53 +00:00
|
|
|
htrd->re.type = QSE_HTRE_Q;
|
2012-02-12 13:20:39 +00:00
|
|
|
if (htrd->option & QSE_HTRD_REQUEST)
|
2010-10-22 07:29:12 +00:00
|
|
|
{
|
2012-03-30 06:12:53 +00:00
|
|
|
/* method name must be followed by space */
|
|
|
|
if (!is_space_octet(*p)) goto badre;
|
|
|
|
|
|
|
|
*p = QSE_MT('\0'); /* null-terminate the method name */
|
|
|
|
|
|
|
|
htrd->re.u.q.method.type = qse_mcstrtohttpmethod (&tmp);
|
|
|
|
htrd->re.u.q.method.name = tmp.ptr;
|
2011-07-06 09:45:00 +00:00
|
|
|
}
|
2011-07-22 09:50:38 +00:00
|
|
|
else if ((htrd->option & QSE_HTRD_RESPONSE) &&
|
2011-07-28 08:35:28 +00:00
|
|
|
qse_mbsxcmp (tmp.ptr, tmp.len, QSE_MT("HTTP")) == 0)
|
2011-07-06 09:45:00 +00:00
|
|
|
{
|
|
|
|
/* it begins with HTTP. it may be a response */
|
2012-03-30 06:12:53 +00:00
|
|
|
htrd->re.type = QSE_HTRE_S;
|
2010-10-26 07:04:11 +00:00
|
|
|
}
|
2011-07-06 09:45:00 +00:00
|
|
|
else goto badre;
|
2011-06-23 10:17:35 +00:00
|
|
|
|
2012-03-30 06:12:53 +00:00
|
|
|
if (htrd->re.type == QSE_HTRE_S)
|
2010-10-26 07:04:11 +00:00
|
|
|
{
|
2012-03-30 06:12:53 +00:00
|
|
|
/* response */
|
2011-07-06 09:45:00 +00:00
|
|
|
int n, status;
|
2010-10-26 07:04:11 +00:00
|
|
|
|
2011-08-05 09:43:28 +00:00
|
|
|
if (*p == QSE_MT('/') && p[1] != QSE_MT('\0') && p[2] == QSE_MT('.'))
|
2011-06-23 10:17:35 +00:00
|
|
|
{
|
|
|
|
int q = digit_to_num(p[1]);
|
|
|
|
int w = digit_to_num(p[3]);
|
|
|
|
if (q >= 0 && w >= 0)
|
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->re.version.major = q;
|
|
|
|
htrd->re.version.minor = w;
|
2011-06-23 10:17:35 +00:00
|
|
|
p += 4;
|
|
|
|
}
|
|
|
|
else goto badre;
|
|
|
|
}
|
|
|
|
else goto badre;
|
2010-10-26 07:04:11 +00:00
|
|
|
|
2012-03-30 06:12:53 +00:00
|
|
|
/* version must be followed by space */
|
2011-06-23 10:17:35 +00:00
|
|
|
if (!is_space_octet(*p)) goto badre;
|
2010-10-22 07:29:12 +00:00
|
|
|
|
2012-03-30 06:12:53 +00:00
|
|
|
*p = QSE_MT('\0'); /* null-terminate version string */
|
|
|
|
htrd->re.verstr = tmp.ptr;
|
|
|
|
|
2011-06-23 10:17:35 +00:00
|
|
|
/* skip spaces */
|
|
|
|
do p++; while (is_space_octet(*p));
|
|
|
|
|
|
|
|
n = digit_to_num(*p);
|
|
|
|
if (n <= -1) goto badre;
|
|
|
|
|
2012-03-30 06:12:53 +00:00
|
|
|
tmp.ptr = p;
|
2011-07-06 09:45:00 +00:00
|
|
|
status = 0;
|
2011-06-23 10:17:35 +00:00
|
|
|
do
|
|
|
|
{
|
2011-07-06 09:45:00 +00:00
|
|
|
status = status * 10 + n;
|
2011-06-23 10:17:35 +00:00
|
|
|
p++;
|
|
|
|
}
|
|
|
|
while ((n = digit_to_num(*p)) >= 0);
|
|
|
|
|
2012-03-30 06:12:53 +00:00
|
|
|
if (!is_space_octet(*p)) goto badre;
|
|
|
|
*p = QSE_MT('\0'); /* null-terminate the status code */
|
|
|
|
|
|
|
|
htrd->re.u.s.code.val = status;
|
|
|
|
htrd->re.u.s.code.str = tmp.ptr;
|
2011-07-06 09:45:00 +00:00
|
|
|
|
2012-03-30 06:12:53 +00:00
|
|
|
/* i don't treat the following weird messages as bad message:
|
2011-08-05 09:43:28 +00:00
|
|
|
* no status message follows the status code
|
|
|
|
*/
|
2011-06-23 10:17:35 +00:00
|
|
|
|
2011-08-05 09:43:28 +00:00
|
|
|
/* skip spaces */
|
2012-03-30 06:12:53 +00:00
|
|
|
do p++; while (is_space_octet(*p));
|
2011-08-05 09:43:28 +00:00
|
|
|
|
2011-07-06 09:45:00 +00:00
|
|
|
tmp.ptr = p;
|
2012-03-30 06:12:53 +00:00
|
|
|
tmp.len = 0;
|
|
|
|
while (*p != QSE_MT('\0') && *p != QSE_MT('\n'))
|
2010-10-26 07:04:11 +00:00
|
|
|
{
|
2012-03-30 06:12:53 +00:00
|
|
|
if (!is_space_octet(*p)) tmp.len = p - tmp.ptr + 1;
|
|
|
|
p++;
|
2011-06-23 10:17:35 +00:00
|
|
|
}
|
2012-03-30 06:12:53 +00:00
|
|
|
|
|
|
|
/* if the line does not end with a new line, it is a bad request */
|
2016-04-26 13:30:29 +00:00
|
|
|
if (*p != QSE_MT('\n')) goto badre;
|
2012-03-30 06:12:53 +00:00
|
|
|
|
|
|
|
/* null-terminate the message */
|
|
|
|
((qse_mchar_t*)tmp.ptr)[tmp.len] = QSE_MT('\0');
|
|
|
|
htrd->re.u.s.mesg = tmp.ptr;
|
2011-06-23 10:17:35 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-09-14 16:18:35 +00:00
|
|
|
#if 0
|
2011-07-28 08:35:28 +00:00
|
|
|
qse_mchar_t* out;
|
2012-09-14 16:18:35 +00:00
|
|
|
#endif
|
2014-07-08 14:30:42 +00:00
|
|
|
qse_mcstr_t param;
|
2011-07-06 09:45:00 +00:00
|
|
|
|
2011-06-23 10:17:35 +00:00
|
|
|
/* skip spaces */
|
|
|
|
do p++; while (is_space_octet(*p));
|
|
|
|
|
|
|
|
/* process the url part */
|
2011-07-06 09:45:00 +00:00
|
|
|
tmp.ptr = p; /* remember the beginning of path*/
|
|
|
|
param.ptr = QSE_NULL;
|
2012-04-15 08:36:14 +00:00
|
|
|
#if 0
|
2011-07-06 09:45:00 +00:00
|
|
|
out = p;
|
2011-08-05 09:43:28 +00:00
|
|
|
while (*p != QSE_MT('\0') && !is_space_octet(*p))
|
2011-06-23 10:17:35 +00:00
|
|
|
{
|
2011-08-05 09:43:28 +00:00
|
|
|
if (*p == QSE_MT('%') && param.ptr == QSE_NULL)
|
2010-10-22 07:29:12 +00:00
|
|
|
{
|
2012-03-30 06:12:53 +00:00
|
|
|
/* decode percent-encoded charaters in the
|
|
|
|
* path part. if we're in the parameter string
|
2011-07-08 10:16:19 +00:00
|
|
|
* part, we don't decode them. */
|
|
|
|
|
2011-06-23 10:17:35 +00:00
|
|
|
int q = xdigit_to_num(*(p+1));
|
|
|
|
int w = xdigit_to_num(*(p+2));
|
2014-11-14 16:56:51 +00:00
|
|
|
|
2011-06-23 10:17:35 +00:00
|
|
|
if (q >= 0 && w >= 0)
|
2010-10-22 07:29:12 +00:00
|
|
|
{
|
2011-06-23 10:17:35 +00:00
|
|
|
int t = (q << 4) + w;
|
|
|
|
if (t == 0)
|
|
|
|
{
|
|
|
|
/* percent enconding contains a null character */
|
|
|
|
goto badre;
|
|
|
|
}
|
2012-04-15 08:36:14 +00:00
|
|
|
|
2011-07-06 09:45:00 +00:00
|
|
|
*out++ = t;
|
2011-06-23 10:17:35 +00:00
|
|
|
p += 3;
|
2014-11-14 16:56:51 +00:00
|
|
|
|
|
|
|
htrd->re.flags |= QSE_HTRE_QPATH_PERDEC;
|
2010-10-22 07:29:12 +00:00
|
|
|
}
|
2011-07-06 09:45:00 +00:00
|
|
|
else *out++ = *p++;
|
2010-10-26 07:04:11 +00:00
|
|
|
}
|
2011-08-05 09:43:28 +00:00
|
|
|
else if (*p == QSE_MT('?'))
|
2011-06-23 10:17:35 +00:00
|
|
|
{
|
2012-04-15 08:36:14 +00:00
|
|
|
if (param.ptr == QSE_NULL)
|
2011-06-23 10:17:35 +00:00
|
|
|
{
|
2012-03-30 06:12:53 +00:00
|
|
|
/* ? must be explicit to be an argument instroducer.
|
2011-06-23 10:17:35 +00:00
|
|
|
* %3f is just a literal. */
|
2011-07-06 09:45:00 +00:00
|
|
|
tmp.len = out - tmp.ptr;
|
2011-08-05 09:43:28 +00:00
|
|
|
*out++ = QSE_MT('\0'); /* null-terminate the path part */
|
2011-07-06 09:45:00 +00:00
|
|
|
param.ptr = out;
|
2011-06-23 10:17:35 +00:00
|
|
|
p++;
|
|
|
|
}
|
2011-07-06 09:45:00 +00:00
|
|
|
else *out++ = *p++;
|
2011-06-23 10:17:35 +00:00
|
|
|
}
|
2011-07-06 09:45:00 +00:00
|
|
|
else *out++ = *p++;
|
2010-10-26 07:04:11 +00:00
|
|
|
}
|
2012-04-15 08:36:14 +00:00
|
|
|
|
2011-06-23 10:17:35 +00:00
|
|
|
/* the url must be followed by a space */
|
|
|
|
if (!is_space_octet(*p)) goto badre;
|
|
|
|
|
2011-07-06 09:45:00 +00:00
|
|
|
/* null-terminate the url part though we know the length */
|
2011-08-05 09:43:28 +00:00
|
|
|
*out = QSE_MT('\0');
|
2011-07-06 09:45:00 +00:00
|
|
|
|
2014-11-14 16:56:51 +00:00
|
|
|
if (htrd->re.flags & QSE_HTRE_QPATH_PERDEC)
|
|
|
|
{
|
|
|
|
/* TODO: build the original qpath */
|
|
|
|
htrd->re.orgpqath.ptr = XXX;
|
|
|
|
htrd->re.orgpath.len = XXXX;
|
|
|
|
}
|
|
|
|
|
2011-07-06 09:45:00 +00:00
|
|
|
if (param.ptr)
|
|
|
|
{
|
|
|
|
param.len = out - param.ptr;
|
2014-11-14 16:56:51 +00:00
|
|
|
htrd->re.u.q.path = tmp;
|
|
|
|
htrd->re.u.q.param = param;
|
2012-03-30 06:12:53 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tmp.len = out - tmp.ptr;
|
2014-11-14 16:56:51 +00:00
|
|
|
htrd->re.u.q.path = tmp;
|
|
|
|
htrd->re.u.q.param.ptr = QSE_NULL;
|
|
|
|
htrd->re.u.q.param.len = 0;
|
2011-07-06 09:45:00 +00:00
|
|
|
}
|
2012-04-15 08:36:14 +00:00
|
|
|
#else
|
|
|
|
while (*p != QSE_MT('\0') && !is_space_octet(*p))
|
|
|
|
{
|
|
|
|
if (*p == QSE_MT('?') && param.ptr == QSE_NULL)
|
|
|
|
{
|
2014-11-14 16:56:51 +00:00
|
|
|
tmp.len = p - tmp.ptr; /* length of the path part */
|
2012-04-15 08:36:14 +00:00
|
|
|
*p++ = QSE_MT('\0'); /* null-terminate the path part */
|
|
|
|
param.ptr = p;
|
|
|
|
}
|
|
|
|
else p++;
|
|
|
|
}
|
2011-07-06 09:45:00 +00:00
|
|
|
|
2012-04-15 08:36:14 +00:00
|
|
|
/* the url must be followed by a space */
|
|
|
|
if (!is_space_octet(*p)) goto badre;
|
2020-05-20 07:26:49 +00:00
|
|
|
if (param.ptr)
|
|
|
|
{
|
|
|
|
param.len = p - param.ptr; /* length of the param part */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tmp.len = p - tmp.ptr;
|
|
|
|
param.len = 0;
|
|
|
|
}
|
2012-04-15 08:36:14 +00:00
|
|
|
*p = QSE_MT('\0'); /* null-terminate the path or param part */
|
|
|
|
|
|
|
|
if (param.ptr)
|
|
|
|
{
|
2014-11-14 16:56:51 +00:00
|
|
|
htrd->re.u.q.path = tmp;
|
|
|
|
htrd->re.u.q.param = param;
|
2012-04-15 08:36:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-11-14 16:56:51 +00:00
|
|
|
htrd->re.u.q.path = tmp;
|
|
|
|
htrd->re.u.q.param.ptr = QSE_NULL;
|
|
|
|
htrd->re.u.q.param.len = 0;
|
2012-04-15 08:36:14 +00:00
|
|
|
}
|
|
|
|
#endif
|
2012-09-12 15:47:41 +00:00
|
|
|
|
|
|
|
if (htrd->option & QSE_HTRD_CANONQPATH)
|
2014-08-08 18:53:04 +00:00
|
|
|
{
|
2014-11-14 16:56:51 +00:00
|
|
|
qse_mchar_t* qpath = htrd->re.u.q.path.ptr;
|
2014-08-08 18:53:04 +00:00
|
|
|
|
|
|
|
/* if the url begins with xxx://,
|
|
|
|
* skip xxx:/ and canonicalize from the second slash */
|
|
|
|
while (is_alpha_octet(*qpath)) qpath++;
|
|
|
|
if (qse_mbszcmp (qpath, QSE_MT("://"), 3) == 0)
|
2014-11-14 16:56:51 +00:00
|
|
|
{
|
2014-08-08 18:53:04 +00:00
|
|
|
qpath = qpath + 2; /* set the position to the second / in :// */
|
2014-11-14 16:56:51 +00:00
|
|
|
htrd->re.u.q.path.len = qse_canonmbspath (qpath, qpath, 0);
|
|
|
|
htrd->re.u.q.path.len += qpath - htrd->re.u.q.path.ptr;
|
|
|
|
}
|
2014-08-08 18:53:04 +00:00
|
|
|
else
|
2014-11-14 16:56:51 +00:00
|
|
|
{
|
|
|
|
qpath = htrd->re.u.q.path.ptr;
|
|
|
|
htrd->re.u.q.path.len = qse_canonmbspath (qpath, qpath, 0);
|
|
|
|
}
|
2014-08-08 18:53:04 +00:00
|
|
|
}
|
2012-04-15 08:36:14 +00:00
|
|
|
|
2011-06-23 10:17:35 +00:00
|
|
|
/* skip spaces after the url part */
|
|
|
|
do { p++; } while (is_space_octet(*p));
|
|
|
|
|
2012-03-30 06:12:53 +00:00
|
|
|
tmp.ptr = p;
|
2011-08-05 09:43:28 +00:00
|
|
|
/* check protocol version */
|
2011-06-23 10:17:35 +00:00
|
|
|
if ((p[0] == 'H' || p[0] == 'h') &&
|
|
|
|
(p[1] == 'T' || p[1] == 't') &&
|
|
|
|
(p[2] == 'T' || p[2] == 't') &&
|
|
|
|
(p[3] == 'P' || p[3] == 'p') &&
|
|
|
|
p[4] == '/' && p[6] == '.')
|
2010-10-26 07:04:11 +00:00
|
|
|
{
|
2011-06-23 10:17:35 +00:00
|
|
|
int q = digit_to_num(p[5]);
|
|
|
|
int w = digit_to_num(p[7]);
|
|
|
|
if (q >= 0 && w >= 0)
|
2010-10-26 07:04:11 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->re.version.major = q;
|
|
|
|
htrd->re.version.minor = w;
|
2011-06-23 10:17:35 +00:00
|
|
|
p += 8;
|
2010-10-22 07:29:12 +00:00
|
|
|
}
|
2011-06-23 10:17:35 +00:00
|
|
|
else goto badre;
|
2010-10-26 07:04:11 +00:00
|
|
|
}
|
2011-06-23 10:17:35 +00:00
|
|
|
else goto badre;
|
|
|
|
|
2012-03-30 06:12:53 +00:00
|
|
|
tmp.len = p - tmp.ptr;
|
|
|
|
|
2011-06-23 10:17:35 +00:00
|
|
|
/* skip trailing spaces on the line */
|
|
|
|
while (is_space_octet(*p)) p++;
|
2010-10-26 07:04:11 +00:00
|
|
|
|
2012-03-30 06:12:53 +00:00
|
|
|
/* if the line does not end with a new line, it is a bad request */
|
2016-04-26 13:30:29 +00:00
|
|
|
if (*p != QSE_MT('\n')) goto badre;
|
2012-03-30 06:12:53 +00:00
|
|
|
|
|
|
|
((qse_mchar_t*)tmp.ptr)[tmp.len] = QSE_MT('\0');
|
|
|
|
htrd->re.verstr = tmp.ptr;
|
2010-10-26 07:04:11 +00:00
|
|
|
}
|
2011-06-23 10:17:35 +00:00
|
|
|
|
2012-03-31 14:59:51 +00:00
|
|
|
/* adjust Connection: Keep-Alive for HTTP 1.1 or later.
|
|
|
|
* this is initial. it can be adjusted further in capture_connection(). */
|
2012-03-30 06:12:53 +00:00
|
|
|
if (htrd->re.version.major > 1 ||
|
|
|
|
(htrd->re.version.major == 1 && htrd->re.version.minor >= 1))
|
|
|
|
{
|
2014-09-17 13:26:21 +00:00
|
|
|
htrd->re.flags |= QSE_HTRE_ATTR_KEEPALIVE;
|
2012-03-30 06:12:53 +00:00
|
|
|
}
|
|
|
|
|
2010-10-26 07:04:11 +00:00
|
|
|
return ++p;
|
|
|
|
|
2011-06-23 10:17:35 +00:00
|
|
|
badre:
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->errnum = QSE_HTRD_EBADRE;
|
2010-10-26 07:04:11 +00:00
|
|
|
return QSE_NULL;
|
|
|
|
}
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
void qse_htrd_clear (qse_htrd_t* htrd)
|
2010-11-26 08:58:36 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
clear_feed (htrd);
|
2014-10-08 14:07:55 +00:00
|
|
|
htrd->flags = 0;
|
2010-11-26 08:58:36 +00:00
|
|
|
}
|
|
|
|
|
2012-11-01 15:03:02 +00:00
|
|
|
qse_mmgr_t* qse_htrd_getmmgr (qse_htrd_t* htrd)
|
|
|
|
{
|
|
|
|
return htrd->mmgr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void* qse_htrd_getxtn (qse_htrd_t* htrd)
|
|
|
|
{
|
|
|
|
return QSE_XTN (htrd);
|
|
|
|
}
|
|
|
|
|
2014-11-03 15:33:15 +00:00
|
|
|
|
|
|
|
qse_htrd_errnum_t qse_htrd_geterrnum (qse_htrd_t* htrd)
|
|
|
|
{
|
|
|
|
return htrd->errnum;
|
|
|
|
}
|
|
|
|
|
2014-11-26 15:14:24 +00:00
|
|
|
int qse_htrd_getopt (qse_htrd_t* htrd)
|
2010-12-01 05:35:28 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
return htrd->option;
|
2010-12-01 05:35:28 +00:00
|
|
|
}
|
|
|
|
|
2014-11-26 15:14:24 +00:00
|
|
|
void qse_htrd_setopt (qse_htrd_t* htrd, int opts)
|
2010-12-01 05:35:28 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->option = opts;
|
2010-12-01 05:35:28 +00:00
|
|
|
}
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
const qse_htrd_recbs_t* qse_htrd_getrecbs (qse_htrd_t* htrd)
|
2010-11-29 08:26:51 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
return htrd->recbs;
|
2010-11-29 08:26:51 +00:00
|
|
|
}
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
void qse_htrd_setrecbs (qse_htrd_t* htrd, const qse_htrd_recbs_t* recbs)
|
2010-11-29 08:26:51 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->recbs = recbs;
|
2010-11-29 08:26:51 +00:00
|
|
|
}
|
|
|
|
|
2012-01-25 15:39:02 +00:00
|
|
|
static int capture_connection (qse_htrd_t* htrd, qse_htb_pair_t* pair)
|
2010-11-04 08:24:29 +00:00
|
|
|
{
|
2012-04-11 15:18:51 +00:00
|
|
|
qse_htre_hdrval_t* val;
|
|
|
|
|
|
|
|
val = QSE_HTB_VPTR(pair);
|
|
|
|
while (val->next) val = val->next;
|
2010-11-11 08:28:38 +00:00
|
|
|
|
2014-10-08 14:07:55 +00:00
|
|
|
/* The value for Connection: may get comma-separated.
|
|
|
|
* so use qse_mbscaseword() instead of qse_mbscmp(). */
|
|
|
|
|
|
|
|
if (qse_mbscaseword (val->ptr, QSE_MT("close"), QSE_MT(',')))
|
2010-11-11 08:28:38 +00:00
|
|
|
{
|
2014-09-17 13:26:21 +00:00
|
|
|
htrd->re.flags &= ~QSE_HTRE_ATTR_KEEPALIVE;
|
2011-06-23 10:17:35 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-10-08 14:07:55 +00:00
|
|
|
if (qse_mbscaseword (val->ptr, QSE_MT("keep-alive"), QSE_MT(',')))
|
2011-06-23 10:17:35 +00:00
|
|
|
{
|
2014-09-17 13:26:21 +00:00
|
|
|
htrd->re.flags |= QSE_HTRE_ATTR_KEEPALIVE;
|
2010-11-11 08:28:38 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-01-25 15:39:02 +00:00
|
|
|
/* Basically i don't care about other values.
|
|
|
|
* but for HTTP 1.0, other values will set connection to 'close'.
|
|
|
|
*
|
|
|
|
* Other values include even Keep-Alive specified multiple times.
|
|
|
|
* Connection: Keep-Alive
|
|
|
|
* Connection: Keep-Alive
|
|
|
|
* For the second Keep-Alive, this function sees 'Keep-Alive,Keep-Alive'
|
|
|
|
* That's because values of the same keys are concatenated.
|
|
|
|
*/
|
|
|
|
if (htrd->re.version.major < 1 ||
|
|
|
|
(htrd->re.version.major == 1 && htrd->re.version.minor <= 0))
|
|
|
|
{
|
2014-09-17 13:26:21 +00:00
|
|
|
htrd->re.flags &= ~QSE_HTRE_ATTR_KEEPALIVE;
|
2012-01-25 15:39:02 +00:00
|
|
|
}
|
2010-11-11 08:28:38 +00:00
|
|
|
return 0;
|
2010-11-04 08:24:29 +00:00
|
|
|
}
|
|
|
|
|
2012-01-25 15:39:02 +00:00
|
|
|
static int capture_content_length (qse_htrd_t* htrd, qse_htb_pair_t* pair)
|
2010-11-04 08:24:29 +00:00
|
|
|
{
|
2010-11-11 08:28:38 +00:00
|
|
|
qse_size_t len = 0, off = 0, tmp;
|
2012-04-11 15:18:51 +00:00
|
|
|
const qse_mchar_t* ptr;
|
|
|
|
qse_htre_hdrval_t* val;
|
|
|
|
|
|
|
|
/* get the last content_length */
|
|
|
|
val = QSE_HTB_VPTR(pair);
|
|
|
|
while (val->next) val = val->next;
|
2010-11-11 08:28:38 +00:00
|
|
|
|
2012-04-11 15:18:51 +00:00
|
|
|
ptr = val->ptr;
|
|
|
|
while (off < val->len)
|
2010-11-11 08:28:38 +00:00
|
|
|
{
|
|
|
|
int num = digit_to_num (ptr[off]);
|
|
|
|
if (num <= -1)
|
|
|
|
{
|
|
|
|
/* the length contains a non-digit */
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->errnum = QSE_HTRD_EBADRE;
|
2010-11-11 08:28:38 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = len * 10 + num;
|
|
|
|
if (tmp < len)
|
|
|
|
{
|
|
|
|
/* the length has overflown */
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->errnum = QSE_HTRD_EBADRE;
|
2010-11-11 08:28:38 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = tmp;
|
|
|
|
off++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (off == 0)
|
|
|
|
{
|
|
|
|
/* no length was provided */
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->errnum = QSE_HTRD_EBADRE;
|
2010-11-11 08:28:38 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-09-17 13:26:21 +00:00
|
|
|
if ((htrd->re.flags & QSE_HTRE_ATTR_CHUNKED) && len > 0)
|
2010-11-25 07:53:55 +00:00
|
|
|
{
|
|
|
|
/* content-length is greater than 0
|
|
|
|
* while transfer-encoding: chunked is specified. */
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->errnum = QSE_HTRD_EBADRE;
|
2010-11-25 07:53:55 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-09-17 13:26:21 +00:00
|
|
|
htrd->re.flags |= QSE_HTRE_ATTR_LENGTH;
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->re.attr.content_length = len;
|
2010-11-11 08:28:38 +00:00
|
|
|
return 0;
|
2010-11-04 08:24:29 +00:00
|
|
|
}
|
|
|
|
|
2012-01-25 15:39:02 +00:00
|
|
|
static int capture_expect (qse_htrd_t* htrd, qse_htb_pair_t* pair)
|
2011-06-23 10:17:35 +00:00
|
|
|
{
|
2012-04-11 15:18:51 +00:00
|
|
|
qse_htre_hdrval_t* val;
|
|
|
|
|
2013-03-22 14:48:57 +00:00
|
|
|
/* Expect is included */
|
2014-09-17 13:26:21 +00:00
|
|
|
htrd->re.flags |= QSE_HTRE_ATTR_EXPECT;
|
2012-04-11 15:18:51 +00:00
|
|
|
|
2013-03-22 14:48:57 +00:00
|
|
|
val = QSE_HTB_VPTR(pair);
|
|
|
|
while (val)
|
|
|
|
{
|
|
|
|
/* Expect: 100-continue is included */
|
|
|
|
if (qse_mbscasecmp (val->ptr, QSE_MT("100-continue")) == 0)
|
2014-09-17 13:26:21 +00:00
|
|
|
htrd->re.flags |= QSE_HTRE_ATTR_EXPECT100;
|
2013-03-22 14:48:57 +00:00
|
|
|
val = val->next;
|
|
|
|
}
|
2012-04-11 15:18:51 +00:00
|
|
|
|
2010-11-25 07:53:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-05 15:41:53 +00:00
|
|
|
static int capture_status (qse_htrd_t* htrd, qse_htb_pair_t* pair)
|
|
|
|
{
|
2012-04-11 15:18:51 +00:00
|
|
|
qse_htre_hdrval_t* val;
|
|
|
|
|
|
|
|
val = QSE_HTB_VPTR(pair);
|
|
|
|
while (val->next) val = val->next;
|
|
|
|
|
|
|
|
htrd->re.attr.status = val->ptr;
|
2012-04-05 15:41:53 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-01-25 15:39:02 +00:00
|
|
|
static int capture_transfer_encoding (qse_htrd_t* htrd, qse_htb_pair_t* pair)
|
2010-11-11 08:28:38 +00:00
|
|
|
{
|
|
|
|
int n;
|
2012-04-11 15:18:51 +00:00
|
|
|
qse_htre_hdrval_t* val;
|
2010-11-11 08:28:38 +00:00
|
|
|
|
2012-04-11 15:18:51 +00:00
|
|
|
val = QSE_HTB_VPTR(pair);
|
|
|
|
while (val->next) val = val->next;
|
|
|
|
|
|
|
|
n = qse_mbscasecmp (val->ptr, QSE_MT("chunked"));
|
2010-11-11 08:28:38 +00:00
|
|
|
if (n == 0)
|
|
|
|
{
|
2011-08-03 10:27:30 +00:00
|
|
|
/* if (htrd->re.attr.content_length > 0) */
|
2014-09-17 13:26:21 +00:00
|
|
|
if (htrd->re.flags & QSE_HTRE_ATTR_LENGTH)
|
2010-11-25 07:53:55 +00:00
|
|
|
{
|
2011-08-03 10:27:30 +00:00
|
|
|
/* both content-length and 'transfer-encoding: chunked' are specified. */
|
2011-06-23 10:17:35 +00:00
|
|
|
goto badre;
|
2010-11-25 07:53:55 +00:00
|
|
|
}
|
|
|
|
|
2014-09-17 13:26:21 +00:00
|
|
|
htrd->re.flags |= QSE_HTRE_ATTR_CHUNKED;
|
2010-11-11 08:28:38 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* other encoding type not supported yet */
|
2011-06-23 10:17:35 +00:00
|
|
|
badre:
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->errnum = QSE_HTRD_EBADRE;
|
2010-11-11 08:28:38 +00:00
|
|
|
return -1;
|
2010-11-04 08:24:29 +00:00
|
|
|
}
|
|
|
|
|
2010-11-11 08:28:38 +00:00
|
|
|
static QSE_INLINE int capture_key_header (
|
2011-07-22 09:50:38 +00:00
|
|
|
qse_htrd_t* htrd, qse_htb_pair_t* pair)
|
2010-11-04 08:24:29 +00:00
|
|
|
{
|
|
|
|
static struct
|
|
|
|
{
|
2011-07-28 08:35:28 +00:00
|
|
|
const qse_mchar_t* ptr;
|
2010-11-04 08:24:29 +00:00
|
|
|
qse_size_t len;
|
2011-07-06 09:45:00 +00:00
|
|
|
int (*handler) (qse_htrd_t*, qse_htb_pair_t*);
|
2010-11-04 08:24:29 +00:00
|
|
|
} hdrtab[] =
|
|
|
|
{
|
2010-11-11 08:28:38 +00:00
|
|
|
{ "Connection", 10, capture_connection },
|
|
|
|
{ "Content-Length", 14, capture_content_length },
|
2011-06-23 10:17:35 +00:00
|
|
|
{ "Expect", 6, capture_expect },
|
2012-04-05 15:41:53 +00:00
|
|
|
{ "Status", 6, capture_status },
|
2010-11-11 08:28:38 +00:00
|
|
|
{ "Transfer-Encoding", 17, capture_transfer_encoding }
|
2010-11-04 08:24:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
int n;
|
|
|
|
qse_size_t mid, count, base = 0;
|
|
|
|
|
|
|
|
/* perform binary search */
|
|
|
|
for (count = QSE_COUNTOF(hdrtab); count > 0; count /= 2)
|
|
|
|
{
|
|
|
|
mid = base + count / 2;
|
|
|
|
|
2012-01-25 15:39:02 +00:00
|
|
|
n = qse_mbsxncasecmp (
|
2011-06-23 10:17:35 +00:00
|
|
|
QSE_HTB_KPTR(pair), QSE_HTB_KLEN(pair),
|
2010-11-04 08:24:29 +00:00
|
|
|
hdrtab[mid].ptr, hdrtab[mid].len
|
|
|
|
);
|
|
|
|
|
|
|
|
if (n == 0)
|
|
|
|
{
|
|
|
|
/* bingo! */
|
2011-07-22 09:50:38 +00:00
|
|
|
return hdrtab[mid].handler (htrd, pair);
|
2010-11-04 08:24:29 +00:00
|
|
|
}
|
2010-11-11 08:28:38 +00:00
|
|
|
|
2010-11-04 08:24:29 +00:00
|
|
|
if (n > 0) { base = mid + 1; count--; }
|
|
|
|
}
|
2010-11-11 08:28:38 +00:00
|
|
|
|
|
|
|
/* No callback functions were interested in this header field. */
|
|
|
|
return 0;
|
2010-11-04 08:24:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct hdr_cbserter_ctx_t
|
2010-11-01 08:08:15 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
qse_htrd_t* htrd;
|
2011-05-24 10:52:37 +00:00
|
|
|
void* vptr;
|
|
|
|
qse_size_t vlen;
|
2010-11-01 08:08:15 +00:00
|
|
|
};
|
|
|
|
|
2010-11-04 08:24:29 +00:00
|
|
|
static qse_htb_pair_t* hdr_cbserter (
|
2010-11-01 08:08:15 +00:00
|
|
|
qse_htb_t* htb, qse_htb_pair_t* pair,
|
|
|
|
void* kptr, qse_size_t klen, void* ctx)
|
|
|
|
{
|
2010-11-04 08:24:29 +00:00
|
|
|
struct hdr_cbserter_ctx_t* tx = (struct hdr_cbserter_ctx_t*)ctx;
|
2010-11-01 08:08:15 +00:00
|
|
|
|
|
|
|
if (pair == QSE_NULL)
|
|
|
|
{
|
2010-11-04 08:24:29 +00:00
|
|
|
/* the key is new. let's create a new pair. */
|
2010-11-01 08:08:15 +00:00
|
|
|
qse_htb_pair_t* p;
|
2012-04-11 15:18:51 +00:00
|
|
|
qse_htre_hdrval_t *val;
|
|
|
|
|
|
|
|
val = QSE_MMGR_ALLOC (htb->mmgr, QSE_SIZEOF(*val));
|
|
|
|
if (val == QSE_NULL)
|
|
|
|
{
|
|
|
|
tx->htrd->errnum = QSE_HTRD_ENOMEM;
|
|
|
|
return QSE_NULL;
|
|
|
|
}
|
2010-11-04 08:24:29 +00:00
|
|
|
|
2012-04-11 15:18:51 +00:00
|
|
|
QSE_MEMSET (val, 0, QSE_SIZEOF(*val));
|
|
|
|
val->ptr = tx->vptr;
|
|
|
|
val->len = tx->vlen;
|
|
|
|
val->next = QSE_NULL;
|
2010-11-04 08:24:29 +00:00
|
|
|
|
2012-04-11 15:18:51 +00:00
|
|
|
p = qse_htb_allocpair (htb, kptr, klen, val, 0);
|
|
|
|
if (p == QSE_NULL)
|
|
|
|
{
|
|
|
|
QSE_MMGR_FREE (htb->mmgr, val);
|
|
|
|
tx->htrd->errnum = QSE_HTRD_ENOMEM;
|
|
|
|
}
|
2010-11-11 08:28:38 +00:00
|
|
|
else
|
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
if (capture_key_header (tx->htrd, p) <= -1)
|
2010-11-11 08:28:38 +00:00
|
|
|
{
|
|
|
|
/* Destroy the pair created here
|
|
|
|
* as it is not added to the hash table yet */
|
|
|
|
qse_htb_freepair (htb, p);
|
|
|
|
p = QSE_NULL;
|
|
|
|
}
|
|
|
|
}
|
2010-11-04 08:24:29 +00:00
|
|
|
|
2010-11-01 08:08:15 +00:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-04-11 15:18:51 +00:00
|
|
|
/* RFC2616
|
|
|
|
* Multiple message-header fields with the same field-name
|
|
|
|
* MAY be present in a message if and only if the entire
|
|
|
|
* field-value for that header field is defined as a
|
|
|
|
* comma-separated list [i.e., #(values)]. It MUST be possible
|
|
|
|
* to combine the multiple header fields into one
|
|
|
|
* "field-name: field-value" pair, without changing the semantics
|
|
|
|
* of the message, by appending each subsequent field-value
|
|
|
|
* to the first, each separated by a comma. The order in which
|
|
|
|
* header fields with the same field-name are received is therefore
|
|
|
|
* significant to the interpretation of the combined field value,
|
|
|
|
* and thus a proxy MUST NOT change the order of these field values
|
|
|
|
* when a message is forwarded.
|
|
|
|
|
|
|
|
* RFC6265 defines the syntax for Set-Cookie and Cookie.
|
|
|
|
* this seems to be conflicting with RFC2616.
|
|
|
|
*
|
|
|
|
* Origin servers SHOULD NOT fold multiple Set-Cookie header fields
|
|
|
|
* into a single header field. The usual mechanism for folding HTTP
|
|
|
|
* headers fields (i.e., as defined in [RFC2616]) might change the
|
|
|
|
* semantics of the Set-Cookie header field because the %x2C (",")
|
|
|
|
* character is used by Set-Cookie in a way that conflicts with
|
|
|
|
* such folding.
|
|
|
|
*
|
2014-10-08 14:07:55 +00:00
|
|
|
* So i just maintain the list of values for a key instead of
|
2012-04-11 15:18:51 +00:00
|
|
|
* folding them.
|
2010-11-01 08:08:15 +00:00
|
|
|
*/
|
|
|
|
|
2012-04-11 15:18:51 +00:00
|
|
|
qse_htre_hdrval_t* val;
|
|
|
|
qse_htre_hdrval_t* tmp;
|
|
|
|
|
|
|
|
val = (qse_htre_hdrval_t*) QSE_MMGR_ALLOC (
|
|
|
|
tx->htrd->mmgr, QSE_SIZEOF(*val));
|
|
|
|
if (val == QSE_NULL)
|
2010-11-01 08:08:15 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
tx->htrd->errnum = QSE_HTRD_ENOMEM;
|
2010-11-01 08:08:15 +00:00
|
|
|
return QSE_NULL;
|
|
|
|
}
|
|
|
|
|
2012-04-11 15:18:51 +00:00
|
|
|
QSE_MEMSET (val, 0, QSE_SIZEOF(*val));
|
|
|
|
val->ptr = tx->vptr;
|
|
|
|
val->len = tx->vlen;
|
|
|
|
val->next = QSE_NULL;
|
2010-11-01 08:08:15 +00:00
|
|
|
|
2012-04-11 15:18:51 +00:00
|
|
|
/* TODO: doubly linked list for speed-up??? */
|
|
|
|
tmp = QSE_HTB_VPTR(pair);
|
|
|
|
QSE_ASSERT (tmp != QSE_NULL);
|
2010-11-01 08:08:15 +00:00
|
|
|
|
2012-04-11 15:18:51 +00:00
|
|
|
/* find the tail */
|
|
|
|
while (tmp->next) tmp = tmp->next;
|
|
|
|
/* append it to the list*/
|
|
|
|
tmp->next = val;
|
2010-11-01 08:08:15 +00:00
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
if (capture_key_header (tx->htrd, pair) <= -1) return QSE_NULL;
|
2010-11-01 08:08:15 +00:00
|
|
|
return pair;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-11 15:18:51 +00:00
|
|
|
qse_mchar_t* parse_header_field (
|
2012-04-06 15:13:53 +00:00
|
|
|
qse_htrd_t* htrd, qse_mchar_t* line, qse_htb_t* tab)
|
2010-10-26 07:04:11 +00:00
|
|
|
{
|
2011-07-28 08:35:28 +00:00
|
|
|
qse_mchar_t* p = line, * last;
|
2010-10-26 07:04:11 +00:00
|
|
|
struct
|
|
|
|
{
|
2011-07-28 08:35:28 +00:00
|
|
|
qse_mchar_t* ptr;
|
2012-04-11 15:18:51 +00:00
|
|
|
qse_size_t len;
|
2010-10-26 07:04:11 +00:00
|
|
|
} name, value;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/* ignore leading spaces excluding crlf */
|
|
|
|
while (is_space_octet(*p)) p++;
|
|
|
|
#endif
|
|
|
|
|
2010-10-27 07:01:16 +00:00
|
|
|
QSE_ASSERT (!is_whspace_octet(*p));
|
|
|
|
|
|
|
|
/* check the field name */
|
|
|
|
name.ptr = last = p;
|
2012-04-11 15:18:51 +00:00
|
|
|
while (*p != QSE_MT('\0') && *p != QSE_MT('\n') && *p != QSE_MT(':'))
|
2010-10-27 07:01:16 +00:00
|
|
|
{
|
|
|
|
if (!is_space_octet(*p++)) last = p;
|
|
|
|
}
|
|
|
|
name.len = last - name.ptr;
|
2010-10-26 07:04:11 +00:00
|
|
|
|
2012-04-15 08:36:14 +00:00
|
|
|
if (*p != QSE_MT(':'))
|
|
|
|
{
|
|
|
|
if (!(htrd->option & QSE_HTRD_STRICT))
|
|
|
|
{
|
|
|
|
while (is_space_octet(*p)) p++;
|
|
|
|
if (*p == QSE_MT('\n'))
|
|
|
|
{
|
|
|
|
/* ignore a line without a colon */
|
|
|
|
p++;
|
2014-10-08 14:07:55 +00:00
|
|
|
return p;
|
2012-04-15 08:36:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
goto badhdr;
|
|
|
|
}
|
2010-10-27 07:01:16 +00:00
|
|
|
*last = '\0';
|
2010-10-26 07:04:11 +00:00
|
|
|
|
|
|
|
/* skip the colon and spaces after it */
|
|
|
|
do { p++; } while (is_space_octet(*p));
|
|
|
|
|
2010-10-27 07:01:16 +00:00
|
|
|
value.ptr = last = p;
|
2012-04-11 15:18:51 +00:00
|
|
|
while (*p != QSE_MT('\0') && *p != QSE_MT('\n'))
|
2010-10-27 07:01:16 +00:00
|
|
|
{
|
|
|
|
if (!is_space_octet(*p++)) last = p;
|
|
|
|
}
|
2010-10-26 07:04:11 +00:00
|
|
|
|
2010-10-27 07:01:16 +00:00
|
|
|
value.len = last - value.ptr;
|
2012-04-11 15:18:51 +00:00
|
|
|
if (*p != QSE_MT('\n')) goto badhdr; /* not ending with a new line */
|
2010-10-26 07:04:11 +00:00
|
|
|
|
2010-10-27 07:01:16 +00:00
|
|
|
/* peep at the beginning of the next line to check if it is
|
|
|
|
* the continuation */
|
|
|
|
if (is_purespace_octet (*++p))
|
|
|
|
{
|
2014-07-14 17:07:05 +00:00
|
|
|
/* RFC: HTTP/1.0 headers may be folded onto multiple lines if
|
|
|
|
* each continuation line begins with a space or horizontal tab.
|
|
|
|
* All linear whitespace, including folding, has the same semantics
|
|
|
|
* as SP. */
|
2011-07-28 08:35:28 +00:00
|
|
|
qse_mchar_t* cpydst;
|
2010-10-27 07:01:16 +00:00
|
|
|
|
|
|
|
cpydst = p - 1;
|
2012-04-11 15:18:51 +00:00
|
|
|
if (*(cpydst-1) == QSE_MT('\r')) cpydst--;
|
2010-10-27 07:01:16 +00:00
|
|
|
|
|
|
|
/* process all continued lines */
|
|
|
|
do
|
|
|
|
{
|
2012-04-11 15:18:51 +00:00
|
|
|
while (*p != QSE_MT('\0') && *p != QSE_MT('\n'))
|
2010-10-27 07:01:16 +00:00
|
|
|
{
|
|
|
|
*cpydst = *p++;
|
|
|
|
if (!is_space_octet(*cpydst++)) last = cpydst;
|
|
|
|
}
|
|
|
|
|
|
|
|
value.len = last - value.ptr;
|
2012-04-11 15:18:51 +00:00
|
|
|
if (*p != QSE_MT('\n')) goto badhdr;
|
2010-10-27 07:01:16 +00:00
|
|
|
|
2012-04-11 15:18:51 +00:00
|
|
|
if (*(cpydst-1) == QSE_MT('\r')) cpydst--;
|
2010-10-27 07:01:16 +00:00
|
|
|
}
|
|
|
|
while (is_purespace_octet(*++p));
|
|
|
|
}
|
2012-04-11 15:18:51 +00:00
|
|
|
*last = QSE_MT('\0');
|
2010-10-27 07:01:16 +00:00
|
|
|
|
2010-11-11 08:28:38 +00:00
|
|
|
/* insert the new field to the header table */
|
|
|
|
{
|
|
|
|
struct hdr_cbserter_ctx_t ctx;
|
2010-11-01 08:08:15 +00:00
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
ctx.htrd = htrd;
|
2010-11-11 08:28:38 +00:00
|
|
|
ctx.vptr = value.ptr;
|
|
|
|
ctx.vlen = value.len;
|
2010-11-01 08:08:15 +00:00
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->errnum = QSE_HTRD_ENOERR;
|
2010-11-11 08:28:38 +00:00
|
|
|
if (qse_htb_cbsert (
|
2012-04-06 15:13:53 +00:00
|
|
|
tab, name.ptr, name.len,
|
2010-11-11 08:28:38 +00:00
|
|
|
hdr_cbserter, &ctx) == QSE_NULL)
|
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
if (htrd->errnum == QSE_HTRD_ENOERR)
|
|
|
|
htrd->errnum = QSE_HTRD_ENOMEM;
|
2010-11-11 08:28:38 +00:00
|
|
|
return QSE_NULL;
|
|
|
|
}
|
2010-10-27 07:01:16 +00:00
|
|
|
}
|
2010-10-26 07:04:11 +00:00
|
|
|
|
|
|
|
return p;
|
|
|
|
|
|
|
|
badhdr:
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->errnum = QSE_HTRD_EBADHDR;
|
2010-10-26 07:04:11 +00:00
|
|
|
return QSE_NULL;
|
|
|
|
}
|
|
|
|
|
2011-06-23 10:17:35 +00:00
|
|
|
static QSE_INLINE int parse_initial_line_and_headers (
|
2011-07-28 08:35:28 +00:00
|
|
|
qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t rlen)
|
2010-11-15 07:54:03 +00:00
|
|
|
{
|
2011-07-28 08:35:28 +00:00
|
|
|
qse_mchar_t* p;
|
2010-11-15 07:54:03 +00:00
|
|
|
|
|
|
|
/* add the actual request */
|
2011-07-22 09:50:38 +00:00
|
|
|
if (push_to_buffer (htrd, &htrd->fed.b.raw, req, rlen) <= -1) return -1;
|
2010-11-15 07:54:03 +00:00
|
|
|
|
|
|
|
/* add the terminating null for easier parsing */
|
2011-07-22 09:50:38 +00:00
|
|
|
if (push_to_buffer (htrd, &htrd->fed.b.raw, &NUL, 1) <= -1) return -1;
|
2010-11-15 07:54:03 +00:00
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
p = QSE_MBS_PTR(&htrd->fed.b.raw);
|
2010-11-15 07:54:03 +00:00
|
|
|
|
2012-04-13 15:21:36 +00:00
|
|
|
#if 0
|
2011-08-02 09:43:48 +00:00
|
|
|
if (htrd->option & QSE_HTRD_SKIPEMPTYLINES)
|
2010-12-01 05:35:28 +00:00
|
|
|
while (is_whspace_octet(*p)) p++;
|
|
|
|
else
|
2012-04-13 15:21:36 +00:00
|
|
|
#endif
|
2010-12-01 05:35:28 +00:00
|
|
|
while (is_space_octet(*p)) p++;
|
|
|
|
|
2010-11-15 07:54:03 +00:00
|
|
|
QSE_ASSERT (*p != '\0');
|
|
|
|
|
2011-06-23 10:17:35 +00:00
|
|
|
/* parse the initial line */
|
2011-08-02 09:43:48 +00:00
|
|
|
if (!(htrd->option & QSE_HTRD_SKIPINITIALLINE))
|
|
|
|
{
|
|
|
|
p = parse_initial_line (htrd, p);
|
|
|
|
if (p == QSE_NULL) return -1;
|
|
|
|
}
|
2010-11-15 07:54:03 +00:00
|
|
|
|
|
|
|
/* parse header fields */
|
|
|
|
do
|
|
|
|
{
|
|
|
|
while (is_whspace_octet(*p)) p++;
|
|
|
|
if (*p == '\0') break;
|
|
|
|
|
|
|
|
/* TODO: return error if protocol is 0.9.
|
|
|
|
* HTTP/0.9 must not get headers... */
|
|
|
|
|
2012-04-11 15:18:51 +00:00
|
|
|
p = parse_header_field (htrd, p, &htrd->re.hdrtab);
|
2010-11-15 07:54:03 +00:00
|
|
|
if (p == QSE_NULL) return -1;
|
|
|
|
}
|
|
|
|
while (1);
|
2012-04-11 15:18:51 +00:00
|
|
|
|
2010-11-15 07:54:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-11-26 08:58:36 +00:00
|
|
|
/* chunk parsing phases */
|
|
|
|
#define GET_CHUNK_DONE 0
|
|
|
|
#define GET_CHUNK_LEN 1
|
|
|
|
#define GET_CHUNK_DATA 2
|
|
|
|
#define GET_CHUNK_CRLF 3
|
|
|
|
#define GET_CHUNK_TRAILERS 4
|
|
|
|
|
2011-07-28 08:35:28 +00:00
|
|
|
static const qse_mchar_t* getchunklen (qse_htrd_t* htrd, const qse_mchar_t* ptr, qse_size_t len)
|
2010-11-25 07:53:55 +00:00
|
|
|
{
|
2011-07-28 08:35:28 +00:00
|
|
|
const qse_mchar_t* end = ptr + len;
|
2010-11-25 07:53:55 +00:00
|
|
|
|
2010-11-26 08:58:36 +00:00
|
|
|
/* this function must be called in the GET_CHUNK_LEN context */
|
2011-07-22 09:50:38 +00:00
|
|
|
QSE_ASSERT (htrd->fed.s.chunk.phase == GET_CHUNK_LEN);
|
2010-11-26 08:58:36 +00:00
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
if (htrd->fed.s.chunk.count <= 0)
|
2010-11-25 07:53:55 +00:00
|
|
|
{
|
2010-11-26 08:58:36 +00:00
|
|
|
/* skip leading spaces if the first character of
|
|
|
|
* the chunk length has not been read yet */
|
2010-11-25 07:53:55 +00:00
|
|
|
while (ptr < end && is_space_octet(*ptr)) ptr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (ptr < end)
|
|
|
|
{
|
|
|
|
int n = xdigit_to_num (*ptr);
|
|
|
|
if (n <= -1) break;
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->fed.s.chunk.len = htrd->fed.s.chunk.len * 16 + n;
|
|
|
|
htrd->fed.s.chunk.count++;
|
2010-11-25 07:53:55 +00:00
|
|
|
ptr++;
|
|
|
|
}
|
|
|
|
|
2010-11-26 08:58:36 +00:00
|
|
|
/* skip trailing spaces if the length has been read */
|
|
|
|
while (ptr < end && is_space_octet(*ptr)) ptr++;
|
2010-11-25 07:53:55 +00:00
|
|
|
|
|
|
|
if (ptr < end)
|
|
|
|
{
|
|
|
|
if (*ptr == '\n')
|
|
|
|
{
|
2010-11-26 08:58:36 +00:00
|
|
|
/* the chunk length line ended properly */
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
if (htrd->fed.s.chunk.count <= 0)
|
2010-11-26 08:58:36 +00:00
|
|
|
{
|
|
|
|
/* empty line - no more chunk */
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->fed.s.chunk.phase = GET_CHUNK_DONE;
|
2010-11-26 08:58:36 +00:00
|
|
|
}
|
2011-07-22 09:50:38 +00:00
|
|
|
else if (htrd->fed.s.chunk.len <= 0)
|
2010-11-26 08:58:36 +00:00
|
|
|
{
|
|
|
|
/* length explicity specified to 0
|
|
|
|
get trailing headers .... */
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->fed.s.chunk.phase = GET_CHUNK_TRAILERS;
|
2010-11-26 08:58:36 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* ready to read the chunk data... */
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->fed.s.chunk.phase = GET_CHUNK_DATA;
|
2010-11-26 08:58:36 +00:00
|
|
|
}
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->fed.s.need = htrd->fed.s.chunk.len;
|
2010-11-25 07:53:55 +00:00
|
|
|
ptr++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->errnum = QSE_HTRD_EBADRE;
|
2010-11-25 07:53:55 +00:00
|
|
|
return QSE_NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
2011-07-28 08:35:28 +00:00
|
|
|
static const qse_mchar_t* get_trailing_headers (
|
|
|
|
qse_htrd_t* htrd, const qse_mchar_t* req, const qse_mchar_t* end)
|
2010-11-27 09:00:57 +00:00
|
|
|
{
|
2011-07-28 08:35:28 +00:00
|
|
|
const qse_mchar_t* ptr = req;
|
2010-11-27 09:00:57 +00:00
|
|
|
|
|
|
|
while (ptr < end)
|
|
|
|
{
|
2011-07-28 08:35:28 +00:00
|
|
|
register qse_mchar_t b = *ptr++;
|
2010-11-27 09:00:57 +00:00
|
|
|
switch (b)
|
|
|
|
{
|
|
|
|
case '\0':
|
|
|
|
/* guarantee that the request does not contain a null
|
|
|
|
* character */
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->errnum = QSE_HTRD_EBADRE;
|
2011-06-23 10:17:35 +00:00
|
|
|
return QSE_NULL;
|
2010-11-27 09:00:57 +00:00
|
|
|
|
|
|
|
case '\n':
|
2011-07-22 09:50:38 +00:00
|
|
|
if (htrd->fed.s.crlf <= 1)
|
2010-11-27 09:00:57 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->fed.s.crlf = 2;
|
2010-11-27 09:00:57 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-07-28 08:35:28 +00:00
|
|
|
qse_mchar_t* p;
|
2010-11-27 09:00:57 +00:00
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
QSE_ASSERT (htrd->fed.s.crlf <= 3);
|
|
|
|
htrd->fed.s.crlf = 0;
|
2010-11-27 09:00:57 +00:00
|
|
|
|
|
|
|
if (push_to_buffer (
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd, &htrd->fed.b.tra, req, ptr - req) <= -1)
|
2010-11-27 09:00:57 +00:00
|
|
|
return QSE_NULL;
|
|
|
|
if (push_to_buffer (
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd, &htrd->fed.b.tra, &NUL, 1) <= -1)
|
2010-11-27 09:00:57 +00:00
|
|
|
return QSE_NULL;
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
p = QSE_MBS_PTR(&htrd->fed.b.tra);
|
2010-11-27 09:00:57 +00:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
while (is_whspace_octet(*p)) p++;
|
|
|
|
if (*p == '\0') break;
|
2014-10-08 14:07:55 +00:00
|
|
|
|
2010-11-27 09:00:57 +00:00
|
|
|
/* TODO: return error if protocol is 0.9.
|
|
|
|
* HTTP/0.9 must not get headers... */
|
2014-10-08 14:07:55 +00:00
|
|
|
|
2012-04-11 15:18:51 +00:00
|
|
|
p = parse_header_field (
|
2012-04-06 15:13:53 +00:00
|
|
|
htrd, p,
|
|
|
|
((htrd->option & QSE_HTRD_TRAILERS)? &htrd->re.trailers: &htrd->re.hdrtab)
|
|
|
|
);
|
2010-11-27 09:00:57 +00:00
|
|
|
if (p == QSE_NULL) return QSE_NULL;
|
|
|
|
}
|
|
|
|
while (1);
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->fed.s.chunk.phase = GET_CHUNK_DONE;
|
2010-11-27 09:00:57 +00:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
case '\r':
|
2011-07-22 09:50:38 +00:00
|
|
|
if (htrd->fed.s.crlf == 0 || htrd->fed.s.crlf == 2)
|
|
|
|
htrd->fed.s.crlf++;
|
|
|
|
else htrd->fed.s.crlf = 1;
|
2010-11-27 09:00:57 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* mark that neither CR nor LF was seen */
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->fed.s.crlf = 0;
|
2012-04-04 08:18:45 +00:00
|
|
|
break;
|
2010-11-27 09:00:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
if (push_to_buffer (htrd, &htrd->fed.b.tra, req, ptr - req) <= -1)
|
2010-11-29 08:26:51 +00:00
|
|
|
return QSE_NULL;
|
2010-11-27 09:00:57 +00:00
|
|
|
|
|
|
|
done:
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
2010-10-26 07:04:11 +00:00
|
|
|
/* feed the percent encoded string */
|
2011-07-28 08:35:28 +00:00
|
|
|
int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
|
2010-10-26 07:04:11 +00:00
|
|
|
{
|
2011-07-28 08:35:28 +00:00
|
|
|
const qse_mchar_t* end = req + len;
|
|
|
|
const qse_mchar_t* ptr = req;
|
2012-02-08 12:59:59 +00:00
|
|
|
int header_completed_during_this_feed = 0;
|
|
|
|
qse_size_t avail;
|
2011-02-22 03:11:21 +00:00
|
|
|
|
2012-04-13 15:21:36 +00:00
|
|
|
QSE_ASSERT (len > 0);
|
|
|
|
|
2014-10-08 14:07:55 +00:00
|
|
|
if (htrd->flags & FEEDING_SUSPENDED)
|
|
|
|
{
|
|
|
|
htrd->errnum = QSE_HTRD_ESUSPENDED;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*if (htrd->option & QSE_HTRD_DUMMY)*/
|
|
|
|
if (htrd->flags & FEEDING_DUMMIFIED)
|
2014-07-14 17:07:05 +00:00
|
|
|
{
|
|
|
|
/* treat everything as contents.
|
|
|
|
* i don't care about headers or whatsoever. */
|
|
|
|
return push_content (htrd, req, len);
|
|
|
|
}
|
|
|
|
|
2010-11-25 07:53:55 +00:00
|
|
|
/* does this goto drop code maintainability? */
|
2011-07-22 09:50:38 +00:00
|
|
|
if (htrd->fed.s.need > 0)
|
2011-02-22 03:11:21 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
/* we're in need of as many octets as htrd->fed.s.need
|
2011-02-22 03:11:21 +00:00
|
|
|
* for contents body. make a proper jump to resume
|
|
|
|
* content handling */
|
|
|
|
goto content_resume;
|
|
|
|
}
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
switch (htrd->fed.s.chunk.phase)
|
2010-11-15 07:54:03 +00:00
|
|
|
{
|
2010-11-25 07:53:55 +00:00
|
|
|
case GET_CHUNK_LEN:
|
|
|
|
goto dechunk_resume;
|
|
|
|
|
|
|
|
case GET_CHUNK_DATA:
|
2011-07-22 09:50:38 +00:00
|
|
|
/* this won't be reached as htrd->fed.s.need
|
2010-11-25 07:53:55 +00:00
|
|
|
* is greater than 0 if GET_CHUNK_DATA is true */
|
|
|
|
goto content_resume;
|
|
|
|
|
|
|
|
case GET_CHUNK_CRLF:
|
|
|
|
goto dechunk_crlf;
|
|
|
|
|
|
|
|
case GET_CHUNK_TRAILERS:
|
2010-11-27 09:00:57 +00:00
|
|
|
goto dechunk_get_trailers;
|
2010-11-15 07:54:03 +00:00
|
|
|
}
|
2010-10-26 07:04:11 +00:00
|
|
|
|
2014-07-14 17:07:05 +00:00
|
|
|
htrd->clean = 0; /* mark that htrd is in need of some data */
|
2012-04-13 15:21:36 +00:00
|
|
|
|
2010-10-26 07:04:11 +00:00
|
|
|
while (ptr < end)
|
|
|
|
{
|
2011-07-28 08:35:28 +00:00
|
|
|
register qse_mchar_t b = *ptr++;
|
2010-10-26 07:04:11 +00:00
|
|
|
|
2012-04-13 15:21:36 +00:00
|
|
|
#if 0
|
2011-08-02 09:43:48 +00:00
|
|
|
if (htrd->option & QSE_HTRD_SKIPEMPTYLINES &&
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->fed.s.plen <= 0 && is_whspace_octet(b))
|
2010-10-26 07:04:11 +00:00
|
|
|
{
|
|
|
|
/* let's drop leading whitespaces across multiple
|
|
|
|
* lines */
|
|
|
|
req++;
|
|
|
|
continue;
|
|
|
|
}
|
2012-04-13 15:21:36 +00:00
|
|
|
#endif
|
2010-10-26 07:04:11 +00:00
|
|
|
|
2010-11-27 09:00:57 +00:00
|
|
|
switch (b)
|
2010-10-26 07:04:11 +00:00
|
|
|
{
|
2014-10-08 14:07:55 +00:00
|
|
|
case QSE_MT('\0'):
|
2010-11-29 08:26:51 +00:00
|
|
|
/* guarantee that the request does not contain
|
|
|
|
* a null character */
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->errnum = QSE_HTRD_EBADRE;
|
2010-11-27 09:00:57 +00:00
|
|
|
return -1;
|
2010-10-26 07:04:11 +00:00
|
|
|
|
2014-10-08 14:07:55 +00:00
|
|
|
case QSE_MT('\n'):
|
2011-06-23 10:17:35 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
if (htrd->fed.s.crlf <= 1)
|
2010-11-11 08:28:38 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
/* htrd->fed.s.crlf == 0
|
2010-11-27 09:00:57 +00:00
|
|
|
* => CR was not seen
|
2011-07-22 09:50:38 +00:00
|
|
|
* htrd->fed.s.crlf == 1
|
2010-11-27 09:00:57 +00:00
|
|
|
* => CR was seen
|
|
|
|
* whatever the current case is,
|
|
|
|
* mark the first LF is seen here.
|
|
|
|
*/
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->fed.s.crlf = 2;
|
2010-11-25 07:53:55 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
/* htrd->fed.s.crlf == 2
|
2010-11-27 09:00:57 +00:00
|
|
|
* => no 2nd CR before LF
|
2011-07-22 09:50:38 +00:00
|
|
|
* htrd->fed.s.crlf == 3
|
2010-11-27 09:00:57 +00:00
|
|
|
* => 2nd CR before LF
|
|
|
|
*/
|
2011-06-23 10:17:35 +00:00
|
|
|
|
2012-02-08 12:59:59 +00:00
|
|
|
/* we got a complete request header. */
|
2011-07-22 09:50:38 +00:00
|
|
|
QSE_ASSERT (htrd->fed.s.crlf <= 3);
|
2010-11-27 09:00:57 +00:00
|
|
|
|
|
|
|
/* reset the crlf state */
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->fed.s.crlf = 0;
|
2010-11-27 09:00:57 +00:00
|
|
|
/* reset the raw request length */
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->fed.s.plen = 0;
|
2014-08-08 18:53:04 +00:00
|
|
|
|
2012-04-09 15:29:33 +00:00
|
|
|
if (parse_initial_line_and_headers (htrd, req, ptr - req) <= -1) return -1;
|
2011-06-23 10:17:35 +00:00
|
|
|
|
2012-02-08 12:59:59 +00:00
|
|
|
/* compelete request header is received */
|
|
|
|
header_completed_during_this_feed = 1;
|
|
|
|
if (htrd->option & QSE_HTRD_PEEKONLY)
|
2011-08-03 10:27:30 +00:00
|
|
|
{
|
2012-03-30 06:12:53 +00:00
|
|
|
/* when QSE_HTRD_PEEKONLY is set,
|
2012-02-08 12:59:59 +00:00
|
|
|
* the peek callback is invoked once
|
|
|
|
* a complete header is seen. the caller
|
|
|
|
* should not feed more data by calling
|
|
|
|
* this function again once the callback is
|
|
|
|
* invoked. the trailing data is appended
|
|
|
|
* to the content buffer.
|
|
|
|
*
|
|
|
|
* NOTE: if the current feed that completed
|
|
|
|
* the header contains the next request,
|
|
|
|
* the next request is treated as if it
|
|
|
|
* belongs to the current request.
|
|
|
|
*
|
|
|
|
* In priciple, this option was added for
|
|
|
|
* reading CGI outputs. So it comes with
|
|
|
|
* awkwardity described above.
|
2011-06-23 10:17:35 +00:00
|
|
|
*/
|
2012-04-13 15:21:36 +00:00
|
|
|
if (ptr < end && push_content (htrd, ptr, end - ptr) <= -1) return -1;
|
2012-02-08 12:59:59 +00:00
|
|
|
|
2014-07-14 17:07:05 +00:00
|
|
|
/* i don't really know if it is really completed
|
2012-02-08 12:59:59 +00:00
|
|
|
* with content. QSE_HTRD_PEEKONLY is not compatible
|
2012-02-09 12:54:00 +00:00
|
|
|
* with the completed state. anyway, let me complete
|
|
|
|
* it. */
|
|
|
|
qse_htre_completecontent (&htrd->re);
|
2012-04-13 15:21:36 +00:00
|
|
|
|
|
|
|
/* this jump is only to invoke the peek
|
|
|
|
* callback. this function should not be fed
|
|
|
|
* more. */
|
2012-02-08 12:59:59 +00:00
|
|
|
goto feedme_more;
|
2011-06-23 10:17:35 +00:00
|
|
|
}
|
2012-02-08 12:59:59 +00:00
|
|
|
|
|
|
|
/* carry on processing content body fed together with the header */
|
2014-09-17 13:26:21 +00:00
|
|
|
if (htrd->re.flags & QSE_HTRE_ATTR_CHUNKED)
|
2010-11-15 07:54:03 +00:00
|
|
|
{
|
2010-11-27 09:00:57 +00:00
|
|
|
/* transfer-encoding: chunked */
|
2014-09-17 13:26:21 +00:00
|
|
|
QSE_ASSERT (!(htrd->re.flags & QSE_HTRE_ATTR_LENGTH));
|
2014-07-14 17:07:05 +00:00
|
|
|
|
2010-11-27 09:00:57 +00:00
|
|
|
dechunk_start:
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->fed.s.chunk.phase = GET_CHUNK_LEN;
|
|
|
|
htrd->fed.s.chunk.len = 0;
|
|
|
|
htrd->fed.s.chunk.count = 0;
|
2014-07-14 17:07:05 +00:00
|
|
|
|
2010-11-27 09:00:57 +00:00
|
|
|
dechunk_resume:
|
2011-07-22 09:50:38 +00:00
|
|
|
ptr = getchunklen (htrd, ptr, end - ptr);
|
2010-11-27 09:00:57 +00:00
|
|
|
if (ptr == QSE_NULL) return -1;
|
2012-04-13 15:21:36 +00:00
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
if (htrd->fed.s.chunk.phase == GET_CHUNK_LEN)
|
2010-11-27 09:00:57 +00:00
|
|
|
{
|
|
|
|
/* still in the GET_CHUNK_LEN state.
|
|
|
|
* the length has been partially read. */
|
|
|
|
goto feedme_more;
|
|
|
|
}
|
2011-07-22 09:50:38 +00:00
|
|
|
else if (htrd->fed.s.chunk.phase == GET_CHUNK_TRAILERS)
|
2010-11-27 09:00:57 +00:00
|
|
|
{
|
2012-04-04 08:18:45 +00:00
|
|
|
/* this state is reached after the
|
|
|
|
* last chunk length 0 is read. The next
|
|
|
|
* empty line immediately completes
|
|
|
|
* a content body. so i need to adjust
|
|
|
|
* this crlf status to 2 as if a trailing
|
|
|
|
* header line has been read. */
|
|
|
|
htrd->fed.s.crlf = 2;
|
|
|
|
|
2010-11-27 09:00:57 +00:00
|
|
|
dechunk_get_trailers:
|
2011-07-22 09:50:38 +00:00
|
|
|
ptr = get_trailing_headers (htrd, ptr, end);
|
2010-11-27 09:00:57 +00:00
|
|
|
if (ptr == QSE_NULL) return -1;
|
2014-07-14 17:07:05 +00:00
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
if (htrd->fed.s.chunk.phase == GET_CHUNK_TRAILERS)
|
2010-11-27 09:00:57 +00:00
|
|
|
{
|
|
|
|
/* still in the same state.
|
|
|
|
* the trailers have not been processed fully */
|
|
|
|
goto feedme_more;
|
|
|
|
}
|
|
|
|
}
|
2010-11-15 07:54:03 +00:00
|
|
|
}
|
2010-11-27 09:00:57 +00:00
|
|
|
else
|
2010-11-15 07:54:03 +00:00
|
|
|
{
|
2011-02-22 03:11:21 +00:00
|
|
|
/* we need to read as many octets as
|
|
|
|
* Content-Length */
|
2012-04-09 15:29:33 +00:00
|
|
|
if ((htrd->option & QSE_HTRD_RESPONSE) &&
|
2014-09-17 13:26:21 +00:00
|
|
|
!(htrd->re.flags & QSE_HTRE_ATTR_LENGTH) &&
|
|
|
|
!(htrd->re.flags & QSE_HTRE_ATTR_KEEPALIVE))
|
2012-04-09 15:29:33 +00:00
|
|
|
{
|
|
|
|
/* for a response, no content-length and
|
|
|
|
* no chunk are specified and 'connection'
|
|
|
|
* is to close. i must read until the
|
|
|
|
* connection is closed. however, there isn't
|
|
|
|
* any good way to know when to stop from
|
|
|
|
* within this function. so the caller
|
|
|
|
* can call qse_htrd_halt() for this. */
|
|
|
|
|
|
|
|
/* set this to the maximum in a type safe way
|
|
|
|
* assuming it's unsigned. the problem of
|
|
|
|
* the current implementation is that
|
|
|
|
* it can't receive more than */
|
|
|
|
htrd->fed.s.need = 0;
|
|
|
|
htrd->fed.s.need = ~htrd->fed.s.need;
|
|
|
|
htrd->fed.s.flags |= CONSUME_UNTIL_CLOSE;
|
|
|
|
}
|
2014-11-04 16:01:00 +00:00
|
|
|
else if ((htrd->option & QSE_HTRD_RESPONSE) &&
|
|
|
|
!(htrd->re.flags & QSE_HTRE_ATTR_LENGTH) &&
|
|
|
|
(htrd->re.flags & QSE_HTRE_ATTR_KEEPALIVE))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* what the hell!
|
|
|
|
* no content-length, but keep-alive and not chunked.
|
|
|
|
* there's no way to know how large the contents is.
|
|
|
|
*
|
|
|
|
* For a request 'http://php.net/manual/en/function.curl-strerror.php' containing the following header fields:
|
|
|
|
* If-Modified-Since: Fri, 31 Oct 2014 11:12:47 GMT
|
|
|
|
* Accept-Encoding: gzip, deflate
|
|
|
|
*
|
|
|
|
* the service gave this response as of this writing:
|
|
|
|
*
|
|
|
|
HTTP/1.1 304 Not Modified
|
|
|
|
Server: nginx/1.6.2
|
|
|
|
Date: Tue, 04 Nov 2014 15:45:46 GMT
|
|
|
|
Connection: keep-alive
|
|
|
|
Vary: Accept-Encoding
|
|
|
|
Set-Cookie: LAST_LANG=en; expires=Wed, 04-Nov-2015 15:45:46 GMT; Max-Age=31536000; path=/; domain=.php.net
|
|
|
|
Set-Cookie: COUNTRY=KOR%2C220.121.110.171; expires=Tue, 11-Nov-2014 15:45:46 GMT; Max-Age=604800; path=/; domain=.php.net
|
|
|
|
|
|
|
|
XXXXXXXX
|
|
|
|
*
|
|
|
|
* XXXXXXX is some compressed garbage included in the contents-body.
|
|
|
|
* why does the service behave this way? is it a server bug or am i doing anything wrong?
|
|
|
|
*
|
|
|
|
* <<WORKAROUND>>
|
|
|
|
* i decided to drop whatever trailing data avaiable
|
|
|
|
* after the header fields for this feeding.
|
|
|
|
* if more contents are fed in later, it will still
|
|
|
|
* end up with a bad request error. */
|
|
|
|
ptr = end;
|
|
|
|
htrd->fed.s.need = 0;
|
|
|
|
}
|
2012-04-09 15:29:33 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
htrd->fed.s.need = htrd->re.attr.content_length;
|
|
|
|
}
|
2010-11-15 07:54:03 +00:00
|
|
|
}
|
2010-11-25 07:53:55 +00:00
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
if (htrd->fed.s.need > 0)
|
2010-11-25 07:53:55 +00:00
|
|
|
{
|
2011-02-22 03:11:21 +00:00
|
|
|
/* content-length or chunked data length
|
|
|
|
* specified */
|
2010-11-27 09:00:57 +00:00
|
|
|
content_resume:
|
|
|
|
avail = end - ptr;
|
2012-04-13 15:21:36 +00:00
|
|
|
if (avail <= 0)
|
|
|
|
{
|
|
|
|
/* we didn't get a complete content yet */
|
|
|
|
|
|
|
|
/* avail can be 0 if data fed ends with
|
|
|
|
* a chunk length withtout actual data.
|
|
|
|
* so i check if avail is greater than 0
|
|
|
|
* in order not to push empty content. */
|
|
|
|
goto feedme_more;
|
|
|
|
}
|
|
|
|
else if (avail < htrd->fed.s.need)
|
2010-11-25 07:53:55 +00:00
|
|
|
{
|
2010-11-27 09:00:57 +00:00
|
|
|
/* the data is not as large as needed */
|
2012-02-09 12:54:00 +00:00
|
|
|
if (push_content (htrd, ptr, avail) <= -1) return -1;
|
2012-04-09 15:29:33 +00:00
|
|
|
|
|
|
|
if (!(htrd->fed.s.flags & CONSUME_UNTIL_CLOSE))
|
|
|
|
{
|
|
|
|
/* i don't decrement htrd->fed.s.need
|
|
|
|
* if i should read until connection is closed.
|
|
|
|
* well, unless set your own callback,
|
|
|
|
* push_content() above will fail
|
|
|
|
* if too much has been received already */
|
|
|
|
htrd->fed.s.need -= avail;
|
|
|
|
}
|
|
|
|
|
2010-11-27 09:00:57 +00:00
|
|
|
/* we didn't get a complete content yet */
|
|
|
|
goto feedme_more;
|
2010-11-25 07:53:55 +00:00
|
|
|
}
|
2010-11-27 09:00:57 +00:00
|
|
|
else
|
2010-11-25 07:53:55 +00:00
|
|
|
{
|
2010-11-27 09:00:57 +00:00
|
|
|
/* we got all or more than needed */
|
2012-02-09 12:54:00 +00:00
|
|
|
if (push_content (htrd, ptr, htrd->fed.s.need) <= -1) return -1;
|
2011-07-22 09:50:38 +00:00
|
|
|
ptr += htrd->fed.s.need;
|
2012-04-09 15:29:33 +00:00
|
|
|
if (!(htrd->fed.s.flags & CONSUME_UNTIL_CLOSE))
|
|
|
|
htrd->fed.s.need = 0;
|
2010-11-25 07:53:55 +00:00
|
|
|
}
|
|
|
|
}
|
2014-10-08 14:07:55 +00:00
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
if (htrd->fed.s.chunk.phase == GET_CHUNK_DATA)
|
2010-11-25 07:53:55 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
QSE_ASSERT (htrd->fed.s.need == 0);
|
|
|
|
htrd->fed.s.chunk.phase = GET_CHUNK_CRLF;
|
2014-07-14 17:07:05 +00:00
|
|
|
|
2010-11-27 09:00:57 +00:00
|
|
|
dechunk_crlf:
|
|
|
|
while (ptr < end && is_space_octet(*ptr)) ptr++;
|
|
|
|
if (ptr < end)
|
|
|
|
{
|
2014-10-08 14:07:55 +00:00
|
|
|
if (*ptr == QSE_MT('\n'))
|
2010-11-27 09:00:57 +00:00
|
|
|
{
|
|
|
|
/* end of chunk data. */
|
|
|
|
ptr++;
|
2014-07-14 17:07:05 +00:00
|
|
|
|
2010-11-27 09:00:57 +00:00
|
|
|
/* more octets still available.
|
|
|
|
* let it decode the next chunk
|
|
|
|
*/
|
|
|
|
if (ptr < end) goto dechunk_start;
|
2014-07-14 17:07:05 +00:00
|
|
|
|
2010-11-27 09:00:57 +00:00
|
|
|
/* no more octets available after
|
|
|
|
* chunk data. the chunk state variables
|
|
|
|
* need to be reset when a jump is made
|
|
|
|
* to dechunk_resume upon the next call
|
|
|
|
*/
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->fed.s.chunk.phase = GET_CHUNK_LEN;
|
|
|
|
htrd->fed.s.chunk.len = 0;
|
|
|
|
htrd->fed.s.chunk.count = 0;
|
2010-11-27 09:00:57 +00:00
|
|
|
|
|
|
|
goto feedme_more;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* redundant character ... */
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->errnum = QSE_HTRD_EBADRE;
|
2010-11-27 09:00:57 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* data not enough */
|
|
|
|
goto feedme_more;
|
|
|
|
}
|
2010-11-25 07:53:55 +00:00
|
|
|
}
|
2010-11-15 07:54:03 +00:00
|
|
|
|
2012-02-09 12:54:00 +00:00
|
|
|
/* the content has been received fully */
|
|
|
|
qse_htre_completecontent (&htrd->re);
|
|
|
|
|
2012-02-08 12:59:59 +00:00
|
|
|
if (header_completed_during_this_feed && htrd->recbs->peek)
|
2010-11-29 08:26:51 +00:00
|
|
|
{
|
2012-02-08 12:59:59 +00:00
|
|
|
/* the peek handler has not been executed.
|
|
|
|
* this can happen if this function is fed with
|
|
|
|
* at least the ending part of a complete header
|
|
|
|
* plus complete content body and the header
|
|
|
|
* of the next request. */
|
2011-06-23 10:17:35 +00:00
|
|
|
int n;
|
2012-02-08 12:59:59 +00:00
|
|
|
htrd->errnum = QSE_HTRD_ENOERR;
|
|
|
|
n = htrd->recbs->peek (htrd, &htrd->re);
|
|
|
|
if (n <= -1)
|
2011-06-23 10:17:35 +00:00
|
|
|
{
|
2012-02-08 12:59:59 +00:00
|
|
|
if (htrd->errnum == QSE_HTRD_ENOERR)
|
|
|
|
htrd->errnum = QSE_HTRD_ERECBS;
|
|
|
|
/* need to clear request on error?
|
|
|
|
clear_feed (htrd); */
|
|
|
|
return -1;
|
2011-06-23 10:17:35 +00:00
|
|
|
}
|
2012-02-08 12:59:59 +00:00
|
|
|
|
|
|
|
header_completed_during_this_feed = 0;
|
|
|
|
}
|
|
|
|
|
2013-02-20 08:47:22 +00:00
|
|
|
if (htrd->recbs->poke)
|
2012-02-08 12:59:59 +00:00
|
|
|
{
|
|
|
|
int n;
|
|
|
|
htrd->errnum = QSE_HTRD_ENOERR;
|
2013-02-20 08:47:22 +00:00
|
|
|
n = htrd->recbs->poke (htrd, &htrd->re);
|
2011-06-23 10:17:35 +00:00
|
|
|
if (n <= -1)
|
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
if (htrd->errnum == QSE_HTRD_ENOERR)
|
|
|
|
htrd->errnum = QSE_HTRD_ERECBS;
|
2011-06-23 10:17:35 +00:00
|
|
|
/* need to clear request on error?
|
2011-07-22 09:50:38 +00:00
|
|
|
clear_feed (htrd); */
|
2011-06-23 10:17:35 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2010-11-29 08:26:51 +00:00
|
|
|
}
|
|
|
|
|
2012-02-08 12:59:59 +00:00
|
|
|
#if 0
|
|
|
|
qse_printf (QSE_T("CONTENT_LENGTH %d, RAW HEADER LENGTH %d\n"),
|
|
|
|
(int)QSE_MBS_LEN(&htrd->re.content),
|
|
|
|
(int)QSE_MBS_LEN(&htrd->fed.b.raw));
|
|
|
|
#endif
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
clear_feed (htrd);
|
2012-04-13 15:21:36 +00:00
|
|
|
if (ptr >= end) return 0; /* no more feeds to handle */
|
2010-11-15 07:54:03 +00:00
|
|
|
|
2014-10-08 14:07:55 +00:00
|
|
|
if (htrd->flags & FEEDING_SUSPENDED)
|
|
|
|
{
|
|
|
|
htrd->errnum = QSE_HTRD_ESUSPENDED;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*if (htrd->option & QSE_HTRD_DUMMY)*/
|
|
|
|
if (htrd->flags & FEEDING_DUMMIFIED)
|
2014-07-14 17:07:05 +00:00
|
|
|
{
|
|
|
|
/* once the mode changes to RAW in a callback,
|
2014-10-08 14:07:55 +00:00
|
|
|
* left-over is pushed as contents */
|
2014-07-14 17:07:05 +00:00
|
|
|
if (ptr < end)
|
|
|
|
return push_content (htrd, ptr, end - ptr);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-27 09:00:57 +00:00
|
|
|
/* let ptr point to the next character to LF or
|
|
|
|
* the optional contents */
|
|
|
|
req = ptr;
|
2012-04-13 15:21:36 +00:00
|
|
|
|
|
|
|
/* since there are more to handle, i mark that
|
|
|
|
* htrd is in need of some data. this may
|
|
|
|
* not be really compatible with SKIPEMPTYLINES.
|
|
|
|
* SHOULD I simply remove the option? */
|
|
|
|
htrd->clean = 0;
|
2010-11-27 09:00:57 +00:00
|
|
|
}
|
|
|
|
break;
|
2011-06-23 10:17:35 +00:00
|
|
|
}
|
2010-11-27 09:00:57 +00:00
|
|
|
|
2014-10-08 14:07:55 +00:00
|
|
|
case QSE_MT('\r'):
|
2011-07-22 09:50:38 +00:00
|
|
|
if (htrd->fed.s.crlf == 0 || htrd->fed.s.crlf == 2)
|
|
|
|
htrd->fed.s.crlf++;
|
|
|
|
else htrd->fed.s.crlf = 1;
|
2010-11-27 09:00:57 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* increment length of a request in raw
|
|
|
|
* excluding crlf */
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->fed.s.plen++;
|
2010-11-27 09:00:57 +00:00
|
|
|
/* mark that neither CR nor LF was seen */
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->fed.s.crlf = 0;
|
2010-10-26 07:04:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ptr > req)
|
|
|
|
{
|
|
|
|
/* enbuffer the incomplete request */
|
2011-07-22 09:50:38 +00:00
|
|
|
if (push_to_buffer (htrd, &htrd->fed.b.raw, req, ptr - req) <= -1) return -1;
|
2010-10-22 07:29:12 +00:00
|
|
|
}
|
|
|
|
|
2010-11-26 08:58:36 +00:00
|
|
|
feedme_more:
|
2012-02-08 12:59:59 +00:00
|
|
|
if (header_completed_during_this_feed && htrd->recbs->peek)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
htrd->errnum = QSE_HTRD_ENOERR;
|
|
|
|
n = htrd->recbs->peek (htrd, &htrd->re);
|
|
|
|
if (n <= -1)
|
|
|
|
{
|
|
|
|
if (htrd->errnum == QSE_HTRD_ENOERR)
|
|
|
|
htrd->errnum = QSE_HTRD_ERECBS;
|
|
|
|
/* need to clear request on error?
|
|
|
|
clear_feed (htrd); */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-22 07:29:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2010-12-01 05:35:28 +00:00
|
|
|
|
2012-04-09 15:29:33 +00:00
|
|
|
int qse_htrd_halt (qse_htrd_t* htrd)
|
|
|
|
{
|
2012-04-14 08:21:07 +00:00
|
|
|
if (htrd->fed.s.flags & CONSUME_UNTIL_CLOSE || !htrd->clean)
|
2012-04-09 15:29:33 +00:00
|
|
|
{
|
|
|
|
qse_htre_completecontent (&htrd->re);
|
|
|
|
|
2013-02-20 08:47:22 +00:00
|
|
|
if (htrd->recbs->poke)
|
2012-04-09 15:29:33 +00:00
|
|
|
{
|
|
|
|
int n;
|
|
|
|
htrd->errnum = QSE_HTRD_ENOERR;
|
2013-02-20 08:47:22 +00:00
|
|
|
n = htrd->recbs->poke (htrd, &htrd->re);
|
2012-04-09 15:29:33 +00:00
|
|
|
if (n <= -1)
|
|
|
|
{
|
|
|
|
if (htrd->errnum == QSE_HTRD_ENOERR)
|
|
|
|
htrd->errnum = QSE_HTRD_ERECBS;
|
|
|
|
/* need to clear request on error?
|
|
|
|
clear_feed (htrd); */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
clear_feed (htrd);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-10-08 14:07:55 +00:00
|
|
|
void qse_htrd_suspend (qse_htrd_t* htrd)
|
|
|
|
{
|
|
|
|
htrd->flags |= FEEDING_SUSPENDED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qse_htrd_resume (qse_htrd_t* htrd)
|
|
|
|
{
|
|
|
|
htrd->flags &= ~FEEDING_SUSPENDED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qse_htrd_dummify (qse_htrd_t* htrd)
|
|
|
|
{
|
|
|
|
htrd->flags |= FEEDING_DUMMIFIED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qse_htrd_undummify (qse_htrd_t* htrd)
|
|
|
|
{
|
|
|
|
htrd->flags &= ~FEEDING_DUMMIFIED;
|
|
|
|
}
|
|
|
|
|
2011-07-29 09:32:56 +00:00
|
|
|
#if 0
|
2014-07-08 14:30:42 +00:00
|
|
|
int qse_htrd_scanqparam (qse_htrd_t* htrd, const qse_mcstr_t* cstr)
|
2011-07-08 10:16:19 +00:00
|
|
|
{
|
2014-07-08 14:30:42 +00:00
|
|
|
qse_mcstr_t key, val;
|
2011-07-28 08:35:28 +00:00
|
|
|
const qse_mchar_t* p, * end;
|
|
|
|
qse_mchar_t* out;
|
2011-07-08 10:16:19 +00:00
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
if (cstr == QSE_NULL) cstr = qse_htre_getqparamcstr(&htrd->re);
|
2011-07-08 10:16:19 +00:00
|
|
|
|
|
|
|
p = cstr->ptr;
|
|
|
|
if (p == QSE_NULL) return 0; /* no param string to scan */
|
|
|
|
|
|
|
|
end = p + cstr->len;
|
|
|
|
|
|
|
|
/* a key and a value pair including two terminating null
|
|
|
|
* can't exceed the the qparamstrlen + 2. only +1 below as there is
|
|
|
|
* one more space for an internal terminating null */
|
2011-07-22 09:50:38 +00:00
|
|
|
qse_mbs_setlen (&htrd->tmp.qparam, cstr->len + 1);
|
2011-07-08 10:16:19 +00:00
|
|
|
|
|
|
|
/* let out point to the beginning of the qparam buffer.
|
|
|
|
* the loop below emits percent-decode key and value to this buffer. */
|
2011-07-22 09:50:38 +00:00
|
|
|
out = QSE_MBS_PTR(&htrd->tmp.qparam);
|
2011-07-08 10:16:19 +00:00
|
|
|
|
|
|
|
key.ptr = out; key.len = 0;
|
|
|
|
val.ptr = QSE_NULL; val.len = 0;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (p >= end || *p == '&' || *p == ';')
|
|
|
|
{
|
|
|
|
QSE_ASSERT (key.ptr != QSE_NULL);
|
|
|
|
|
|
|
|
*out++ = '\0';
|
|
|
|
if (val.ptr == QSE_NULL)
|
|
|
|
{
|
|
|
|
if (key.len == 0)
|
|
|
|
{
|
|
|
|
/* both key and value are empty.
|
|
|
|
* we don't need to do anything */
|
|
|
|
goto next_octet;
|
|
|
|
}
|
|
|
|
|
|
|
|
val.ptr = out;
|
|
|
|
*out++ = '\0';
|
|
|
|
QSE_ASSERT (val.len == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
QSE_ASSERTX (
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->recbs->qparamstr != QSE_NULL,
|
2011-07-08 10:16:19 +00:00
|
|
|
"set request parameter string callback before scanning"
|
|
|
|
);
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
htrd->errnum = QSE_HTRD_ENOERR;
|
|
|
|
if (htrd->recbs->qparamstr (htrd, &key, &val) <= -1)
|
2011-07-08 10:16:19 +00:00
|
|
|
{
|
2011-07-22 09:50:38 +00:00
|
|
|
if (htrd->errnum == QSE_HTRD_ENOERR)
|
|
|
|
htrd->errnum = QSE_HTRD_ERECBS;
|
2011-07-08 10:16:19 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
next_octet:
|
|
|
|
if (p >= end) break;
|
|
|
|
p++;
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
out = QSE_MBS_PTR(&htrd->tmp.qparam);
|
2011-07-08 10:16:19 +00:00
|
|
|
key.ptr = out; key.len = 0;
|
|
|
|
val.ptr = QSE_NULL; val.len = 0;
|
|
|
|
}
|
|
|
|
else if (*p == '=')
|
|
|
|
{
|
|
|
|
*out++ = '\0'; p++;
|
|
|
|
|
|
|
|
val.ptr = out;
|
|
|
|
/*val.len = 0; */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (*p == '%' && p + 2 <= end)
|
|
|
|
{
|
|
|
|
int q = xdigit_to_num(*(p+1));
|
|
|
|
if (q >= 0)
|
|
|
|
{
|
|
|
|
int w = xdigit_to_num(*(p+2));
|
|
|
|
if (w >= 0)
|
|
|
|
{
|
|
|
|
/* unlike the path part, we don't care if it
|
|
|
|
* contains a null character */
|
|
|
|
*out++ = ((q << 4) + w);
|
|
|
|
p += 3;
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*out++ = *p++;
|
|
|
|
|
|
|
|
next:
|
|
|
|
if (val.ptr) val.len++;
|
|
|
|
else key.len++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (1);
|
|
|
|
|
2011-07-22 09:50:38 +00:00
|
|
|
qse_mbs_clear (&htrd->tmp.qparam);
|
2011-07-08 10:16:19 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-07-29 09:32:56 +00:00
|
|
|
#endif
|