summaryrefslogtreecommitdiff
path: root/cl-parser.c
blob: 31e1c7428a093ce19ff2590bf72cfe2472fb63b5 (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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include "internal.h"

//
// content length functions
//

static const fhp_cl_parser_t FHP_DEFAULT_CL_PARSER = {
  .state = FHP_CL_STATE_INIT,
  .val = 0,
  .err = FHP_OK,
};

fhp_err_t
fhp_cl_parser_init(fhp_cl_parser_t * const p) {
  // init parser
  *p = FHP_DEFAULT_CL_PARSER;

  // return succes
  return FHP_OK;
}

static fhp_err_t
fhp_cl_parser_push_byte(
  fhp_cl_parser_t * const p,
  uint8_t byte
) {
retry:
  switch (p->state) {
  case FHP_CL_STATE_INIT:
    switch (byte) {
    CASE_OWS_CHARS
      // ignore
      break;
    CASE_DIGIT_CHARS
      // set state
      p->state = FHP_CL_STATE_DATA;
      goto retry;

      break;
    default:
      // invalid character before content-length
      return FHP_ERR_INVALID_CHAR_BEFORE_CL;
    }

    break;
  case FHP_CL_STATE_DATA:
    switch (byte) {
    CASE_OWS_CHARS
      // set state
      p->state = FHP_CL_STATE_TRAILING_SPACES;
      goto retry;

      break;
    CASE_DIGIT_CHARS
      {
        // cache old value
        uint64_t old_val = p->val;

        // update value
        p->val = p->val * 10 + (byte - '0');

        // check for overflow
        if (p->val < old_val)
          return FHP_ERR_CL_OVERFLOW;
      }

      break;
    default:
      // invalid character in content-length
      return FHP_ERR_INVALID_CHAR_IN_CL;
    }

    break;
  case FHP_CL_STATE_TRAILING_SPACES:
    switch (byte) {
    CASE_OWS_CHARS
      // ignore trailing spaces

      break;
    default:
      // invalid character after content-length
      return FHP_ERR_INVALID_CHAR_AFTER_CL;
    }

    break;
  case FHP_CL_STATE_ERROR:
    // return last error
    return p->err;

    break;
  case FHP_CL_STATE_DONE:
    // shouldn't be reached
    return FHP_ERR_CL_PARSER_DONE;

    break;
  default:
    // never reached
    return FHP_ERR_BAD_CL_STATE;
  }

  // return succes
  return FHP_OK;
}

fhp_err_t
fhp_cl_parser_push(
  fhp_cl_parser_t *p,
  uint8_t * const buf,
  size_t len
) {
  for (size_t i = 0; i < len; i++) {
    // push byte
    fhp_err_t err = fhp_cl_parser_push_byte(p, buf[i]);

    // check error
    if (err != FHP_OK) {
      // cache error
      p->err = err;

      // set state
      p->state = FHP_CL_STATE_ERROR;

      // return error
      return err;
    }
  }

  // return succes
  return FHP_OK;
}

fhp_err_t
fhp_cl_parser_done(
  fhp_cl_parser_t * const p,
  uint64_t * const ret_val
) {
  // check for error
  if (p->err != FHP_OK)
    return p->err;

  // set state
  p->state = FHP_CL_STATE_DONE;

  // save number of tes
  if (ret_val)
    *ret_val = p->val;

  // return success
  return FHP_OK;
}