summaryrefslogtreecommitdiff
path: root/src/libtipidee/tipidee_rql_read.c
blob: fc99f37f4ce4c857f71787c23146906a27998e89 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/* ISC license. */

#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>

#include <skalibs/types.h>
#include <skalibs/bytestr.h>
#include <skalibs/buffer.h>
#include <skalibs/unix-timed.h>

#include <tipidee/method.h>
#include <tipidee/uri.h>
#include <tipidee/rql.h>

static inline uint8_t tokenize_cclass (char c)
{
  switch (c)
  {
    case '\0' : return 0 ;
    case ' ' :
    case '\t' : return 1 ;
    default : return 2 ;
  }
}

static inline int rql_tokenize (char *s, size_t *tab)
{
  uint8_t const table[2][3] =
  {
    { 0x02, 0x00, 0x11 },
    { 0x02, 0x20, 0x01 }
  } ;
  size_t i = 0 ;
  unsigned int tokens = 0 ;
  uint8_t state = 0 ;
  for (; state < 2 ; i++)
  {
    uint8_t c = table[state][tokenize_cclass(s[i])] ;
    state = c & 3 ;
    if (c & 0x10)
    {
      if (tokens >= 3) goto err ;
      tab[tokens++] = i ;
    }
    if (c & 0x20) s[i] = 0 ;
  }
  return 1 ;
 err:
  return 0 ;
}

static inline int get_version (char const *in, tipidee_rql *rql)
{
  size_t l ;
  if (strncmp(in, "HTTP/", 5)) return 0 ;
  in += 5 ;
  l = uint_scan(in, &rql->http_major) ;
  if (!l) return 0 ;
  in += l ;
  if (*in++ != '.') return 0 ;
  return !!uint0_scan(in, &rql->http_minor) ;
}

int tipidee_rql_read (buffer *b, char *buf, size_t max, size_t *w, tipidee_rql *rql, tain const *deadline, tain *stamp)
{
  size_t pos[3] = { 0 } ;
  if (timed_getlnmax(b, buf, max, &pos[0], '\n', deadline, stamp) == -1)
    return errno == ETIMEDOUT ? 99 : -1 ;
  buf[--pos[0]] = 0 ;
  if (buf[pos[0] - 1] == '\r') buf[--pos[0]] = 0 ;
  if (!rql_tokenize(buf, pos)) return 400 ;
  rql->m = tipidee_method_tonum(buf + pos[0]) ;
  if (rql->m == TIPIDEE_METHOD_UNKNOWN) return 400 ;
  if (!get_version(buf + pos[2], rql)) return 400 ;
  if (rql->m != TIPIDEE_METHOD_OPTIONS || strcmp(buf + pos[1], "*"))
  {
    size_t l = tipidee_uri_parse(buf, max, buf + pos[1], &rql->uri) ;
    if (!l) return 400 ;
    *w = l ;
  }
  return 0 ;
}