1 // HTTP server and client
2 
3 #define HTTP_LIMIT  4096
4 #define HTTP_TIME   15000
5 #define HTTP_LISTEN 28888
6 #define HTTP_HDRS   64
7 #define HTTP_PORT   80
8 
9 enum
10 {
11     HTTP_R_NONE = 0,
12     HTTP_R_OK = 200,
13     HTTP_R_NOTFOUND = 404
14 };
15 
16 enum
17 {
18     HTTP_S_START = 0,
19     HTTP_S_HEADERS,
20     HTTP_S_DATA,
21     HTTP_S_RESPONSE,
22     HTTP_S_DONE,
23     HTTP_S_FAILED,
24     HTTP_S_CONNECTING,
25     HTTP_S_MAX
26 };
27 
28 enum
29 {
30     HTTP_T_ERROR = 0,
31     HTTP_T_GET,
32     HTTP_T_POST,
33     HTTP_T_PUT,
34     HTTP_T_DELETE,
35     HTTP_T_MAX
36 };
37 
38 enum
39 {
40     HTTP_C_NONE = 0,
41     HTTP_C_URLENC,
42     HTTP_C_JSON,
43     HTTP_C_DATA,
44     HTTP_C_MAX
45 };
46 
47 struct httppair
48 {
49     string name, value;
50 
httppairhttppair51     httppair()
52     {
53         name[0] = value[0] = 0;
54     }
httppairhttppair55     httppair(const char *n, const char *v)
56     {
57         copystring(name, n);
58         copystring(value, v);
59     }
~httppairhttppair60     ~httppair() {}
61 };
62 
63 struct httpvars
64 {
65     vector<httppair> values;
66 
httpvarshttpvars67     httpvars() { reset(); }
~httpvarshttpvars68     ~httpvars() {}
69 
resethttpvars70     void reset()
71     {
72         values.shrink(0);
73     }
74 
addhttpvars75     httppair &add()
76     {
77         return values.add();
78     }
79 
addhttpvars80     httppair &add(const char *name, const char *value)
81     {
82         return values.add(httppair(name, value));
83     }
84 
findhttpvars85     httppair *find(const char *name)
86     {
87         loopv(values) if(!strcasecmp(name, values[i].name)) return &values[i];
88         return NULL;
89     }
90 
gethttpvars91     const char *get(const char *name)
92     {
93         httppair *v = find(name);
94         if(v) return v->value;
95         return NULL;
96     }
97 
lengthhttpvars98     int length()
99     {
100         return values.length();
101     }
102 };
103 
104 struct httpreq
105 {
106     ENetAddress address;
107     ENetSocket socket;
108     int state, reqtype, contype;
109     string name;
110     bigstring path, input;
111     vector<char> output;
112     int inputpos, outputpos, conlength;
113     enet_uint32 lastactivity;
114     httpvars inhdrs, outhdrs, vars;
115 
httpreqhttpreq116     httpreq() { reset(); }
~httpreqhttpreq117     ~httpreq() {}
118 
resethttpreq119     void reset()
120     {
121         state = HTTP_S_START;
122         reqtype = HTTP_T_ERROR;
123         contype = HTTP_C_NONE;
124         name[0] = path[0] = 0;
125         inputpos = outputpos = conlength = 0;
126         lastactivity = 0;
127         output.shrink(0);
128         inhdrs.reset();
129         outhdrs.reset();
130         vars.reset();
131     }
132 
rawhttpreq133     void raw(const char c)
134     {
135         output.add(c);
136     }
137 
138     void send(const char *msg, int len = 0)
139     {
140         if(!len) len = strlen(msg);
141         output.put(msg, len);
142         output.put("\r\n", 2);
143     }
144 
sendfhttpreq145     void sendf(const char *fmt, ...)
146     {
147         bigstring msg;
148         va_list args;
149         va_start(args, fmt);
150         vformatstring(msg, fmt, args);
151         va_end(args);
152         send(msg);
153     }
154 };
155 
156 struct httpclient;
157 typedef void (__cdecl *httpcb)(httpclient *c);
158 struct httpclient : httpreq
159 {
160     int port, uid;
161     httpcb callback;
162     bigstring data;
163 
httpclienthttpclient164     httpclient() { reset(); }
~httpclienthttpclient165     ~httpclient() {}
166 
resethttpclient167     void reset()
168     {
169         port = 0;
170         uid = -1;
171         data[0] = 0;
172         callback = NULL;
173         httpreq::reset();
174     }
175 };
176 
177 struct httpcmd;
178 typedef void (__cdecl *httpfun)(httpreq *r);
179 struct httpcmd
180 {
181     const char *name;
182     httpfun fun;
183 
httpcmdhttpcmd184     httpcmd() {}
namehttpcmd185     httpcmd(const char *n, void *f = NULL) : name(n), fun((httpfun)f) {}
186 };
187 
188 #define HTTP(name, fun) UNUSED static bool __http_##fun = http::addcommand(name, (httpfun)fun)
189 
190 #define JSON_TOKENS 1024
191 
192 enum
193 {
194     JSON_INIT = 0,
195     JSON_OBJECT,
196     JSON_ARRAY,
197     JSON_STRING,
198     JSON_PRIMITIVE,
199     JSON_MAX
200 };
201 
202 struct jsobj
203 {
204     string name, data;
205     int type;
206     vector<jsobj *> children;
207 
jsobjjsobj208     jsobj() { reset(); }
~jsobjjsobj209     ~jsobj() { clear(); }
210 
clearjsobj211     void clear()
212     {
213         children.deletecontents();
214     }
215 
resetjsobj216     void reset()
217     {
218         name[0] = data[0] = 0;
219         type = JSON_INIT;
220         children.shrink(0);
221     }
222 };
223 
224 namespace http
225 {
226     extern char *urldecode(char *dst, const char *str, size_t len);
227     extern int vardecode(const char *str, httpvars &vars);
228     extern bool addcommand(const char *name, httpfun fun);
229     extern httpclient *retrieve(const char *serv, int port, int type, const char *path, httpcb callback, const char *data = NULL, int uid = -1);
230     extern void cleanup();
231     extern void init();
232     extern void runframe();
233 }
234 
235 namespace json
236 {
237     extern int print(jsobj *j, char *dst, int level, size_t len);
238     extern jsobj *load(const char *str);
239 }
240