├── Makefile
├── Makefile-mqtthttpd
├── README.markdown
├── accesslog.c
├── accesslog.h
├── common
├── b64.c
├── b64.h
├── cJSON.c
├── cJSON.h
├── common.h
├── jitter.c
├── jitter.h
├── logging.c
├── logging.h
├── path.c
├── path.h
├── pidfile.c
├── pidfile.h
├── queue.h
├── random.c
├── random.h
├── tdate_parse.c
├── tdate_parse.h
├── tree.h
├── util.c
└── util.h
├── evhelper.h
├── htdocs
├── about.html
├── api.js
├── index.html
└── jquery.min.js
├── httpd.c
├── httpd.h
├── httpdconnlist.c
├── httpdconnlist.h
├── idset.c
├── idset.h
├── libebb
├── .gitignore
├── LICENSE
├── Makefile
├── README
├── config.mk
├── doc
│ ├── icon.png
│ └── index.html
├── ebb.c
├── ebb.h
├── ebb_request_parser.h
├── ebb_request_parser.rl
├── examples
│ ├── ca-cert.pem
│ ├── ca-key.pem
│ └── hello_world.c
├── rbtree.c
├── rbtree.h
├── test_examples.rb
├── test_rbtree.c
└── test_request_parser.c
├── midconnlist.c
├── midconnlist.h
├── mqtt.c
├── mqtt.h
├── mqtthttpd.c
├── mqtthttpd.h
├── msgcache.c
├── msgcache.h
├── opt.c
├── opt.h
├── stream.c
├── stream.h
├── streamlist.c
├── streamlist.h
├── sublist.c
├── sublist.h
├── tailq.c
├── tailq.h
├── topic.c
├── topic.h
├── webapi.c
├── webapi.h
├── webapi_serve.c
└── webapi_serve.h
/Makefile:
--------------------------------------------------------------------------------
1 | all:
2 | make -C libebb
3 | make -f Makefile-mqtthttpd
4 |
5 | clean:
6 | -make -C libebb clean
7 | -make -f Makefile-mqtthttpd clean
8 | rm -f access.log mqtthttpd.pid
9 |
10 |
--------------------------------------------------------------------------------
/Makefile-mqtthttpd:
--------------------------------------------------------------------------------
1 | TARGET=mqtthttpd
2 |
3 | CC = gcc
4 | OBJDIR=obj
5 | CFLAGS += -Wl,-Map=$(TARGET).map -Wall -g -O2 -I. -Ilibebb -DHAVE_GNUTLS -D_GNU_SOURCE
6 | CFLAGS += -Werror
7 | LDFLAGS = -lev -lm -Llibebb -lebb -lpthread -lssl -lmosquitto -luriparser -lgnutls #-lefence
8 |
9 | vpath %.c common
10 | vpath %.h common
11 |
12 | OBJS=\
13 | $(OBJDIR)/$(TARGET).o \
14 | $(OBJDIR)/mqtt.o \
15 | $(OBJDIR)/logging.o \
16 | $(OBJDIR)/util.o \
17 | $(OBJDIR)/jitter.o \
18 | $(OBJDIR)/pidfile.o \
19 | $(OBJDIR)/httpdconnlist.o \
20 | $(OBJDIR)/b64.o \
21 | $(OBJDIR)/tdate_parse.o \
22 | $(OBJDIR)/httpd.o \
23 | $(OBJDIR)/webapi.o \
24 | $(OBJDIR)/midconnlist.o \
25 | $(OBJDIR)/streamlist.o \
26 | $(OBJDIR)/stream.o \
27 | $(OBJDIR)/topic.o \
28 | $(OBJDIR)/sublist.o \
29 | $(OBJDIR)/cJSON.o \
30 | $(OBJDIR)/webapi_serve.o \
31 | $(OBJDIR)/path.o \
32 | $(OBJDIR)/idset.o \
33 | $(OBJDIR)/tailq.o \
34 | $(OBJDIR)/random.o \
35 | $(OBJDIR)/msgcache.o \
36 | $(OBJDIR)/accesslog.o \
37 | $(OBJDIR)/opt.o
38 |
39 | all: $(TARGET)
40 |
41 | $(TARGET): $(OBJS)
42 | $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(TARGET)
43 |
44 | $(OBJDIR)/%.o : %.c *.h common/*.h
45 | @mkdir -p $(OBJDIR)
46 | $(CC) $(CFLAGS) -o $@ -c $<
47 |
48 | clean:
49 | rm -rf $(OBJDIR) $(TARGET) package $(TARGET).map
50 |
51 |
52 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | mqtt-http-server
2 | ================
3 | Toby Jaffey
4 |
5 | An MQTT to HTTP bridge, allowing web clients access to MQTT.
6 | Supporting long polling, JSON, Javascript API, SSL.
7 |
8 | More documentation is needed. For now, see the examples in htdocs.
9 |
10 | Building
11 | --------
12 |
13 | Install dependencies
14 |
15 | sudo apt-get install make gcc libgnutls-dev ragel liburiparser-dev libmosquitto0-dev libev-dev libssl-dev
16 |
17 | Build it
18 |
19 | make
20 |
21 | Run it
22 |
23 | ./mqtthttpd -v -r htdocs -l 8080 -n foo -s test.mosquitto.org -p 1883
24 |
25 | Point a web browser at:
26 |
27 | http://localhost:8080
28 |
29 |
30 | HTTPS
31 | -----
32 |
33 | To run with HTTPS, provide a certificate and key, eg.
34 |
35 | ./mqtthttpd -v -r htdocs -l 443 -n foo -s test.mosquitto.org -p 1883 -c ./libebb/examples/ca-cert.pem -k ./libebb/examples/ca-key.pem
36 |
37 |
38 | Using ports 80 and 443
39 | ----------------------
40 |
41 | mqtthttpd should not be run as root. Install authbind (apt-get install authbind) to allow users to run services on low numbered ports.
42 |
43 | sudo apt-get install authbind
44 | sudo touch /etc/authbind/byport/80
45 | sudo chmod 755 /etc/authbind/byport/80
46 | sudo chown username.username /etc/authbind/byport/80
47 | authbind ./mqtthttpd -v -r htdocs -l 80 -n foo -s test.mosquitto.org -p 1883
48 |
49 |
50 |
--------------------------------------------------------------------------------
/accesslog.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #include "accesslog.h"
27 |
28 | #define ACCESSLOG_FILENAME "access.log"
29 | static FILE *fp;
30 |
31 | int accesslog_init(void)
32 | {
33 | if (NULL == (fp = fopen(ACCESSLOG_FILENAME, "a")))
34 | return 1;
35 | return 0;
36 | }
37 |
38 | void accesslog_write(const char *format, ...)
39 | {
40 | va_list args;
41 | const char *timeformat = "%Y-%m-%dT%H:%M:%S";
42 | char timestr[256];
43 | time_t t;
44 |
45 | t = time(NULL);
46 | strftime(timestr, sizeof(timestr), timeformat, gmtime(&t));
47 |
48 | va_start(args, format);
49 | fprintf(fp, "[%s] ", timestr);
50 | vfprintf(fp, format, args);
51 | fprintf(fp, "\r\n");
52 | va_end(args);
53 |
54 | fflush(fp);
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/accesslog.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef ACCESSLOG_H
17 | #define ACCESSLOG_H 1
18 |
19 | extern int accesslog_init(void);
20 | extern void accesslog_write(const char *format, ...);
21 |
22 | #endif
23 |
24 |
--------------------------------------------------------------------------------
/common/b64.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
8 | static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
9 |
10 | static void b64_decodeblock( uint8_t in[4], uint8_t out[3] )
11 | {
12 | out[0] = (uint8_t) (in[0] << 2 | in[1] >> 4);
13 | out[1] = (uint8_t) (in[1] << 4 | in[2] >> 2);
14 | out[2] = (uint8_t) (((in[2] << 6) & 0xc0) | in[3]);
15 | }
16 |
17 | static void encodeblock( uint8_t in[3], uint8_t out[4], size_t len )
18 | {
19 | out[0] = cb64[ in[0] >> 2 ];
20 | out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
21 | out[2] = (uint8_t) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
22 | out[3] = (uint8_t) (len > 2 ? cb64[ in[2] & 0x3f ] : '=');
23 | }
24 |
25 | size_t b64_decode(char *outstr, const char *indata, size_t inlen)
26 | {
27 | uint8_t in[4], out[3], v;
28 | size_t i, len;
29 | char *orig_outstr = outstr;
30 | size_t inp = 0;
31 |
32 | while( inp <= inlen )
33 | {
34 | for( len = 0, i = 0; i < 4 && inp <= inlen; i++ )
35 | {
36 | v = 0;
37 | while( inp <= inlen && v == 0 )
38 | {
39 | v = (uint8_t) indata[inp++];
40 | v = (uint8_t) ((v < 43 || v > 122) ? 0 : cd64[ v - 43 ]);
41 | if( v )
42 | {
43 | v = (uint8_t) ((v == '$') ? 0 : v - 61);
44 | }
45 | }
46 | if( inp <= inlen )
47 | {
48 | len++;
49 | if( v )
50 | {
51 | in[ i ] = (uint8_t) (v - 1);
52 | }
53 | }
54 | else
55 | {
56 | in[i] = 0;
57 | }
58 | }
59 | if( len )
60 | {
61 | b64_decodeblock( in, out );
62 | for( i = 0; i < len - 1; i++ )
63 | {
64 | *outstr++ = out[i];
65 | }
66 | }
67 | }
68 |
69 | len = (outstr - orig_outstr);
70 |
71 | while((size_t)(outstr - orig_outstr) < inlen)
72 | {
73 | *outstr++ = 0; // pad with terminator
74 | }
75 |
76 | return len;
77 | }
78 |
79 |
80 | void b64_encode( char *dest, char *src, size_t src_len )
81 | {
82 | uint8_t in[3], out[4];
83 | size_t i, len;
84 | char *src_end = src + src_len;
85 |
86 | while( src <= src_end )
87 | {
88 | len = 0;
89 | for( i = 0; i < 3; i++ )
90 | {
91 | in[i] = (uint8_t)*src;
92 | src++;
93 | if( src <= src_end )
94 | {
95 | len++;
96 | }
97 | else
98 | {
99 | in[i] = 0;
100 | }
101 | }
102 | if( len )
103 | {
104 | encodeblock( in, out, len );
105 | for( i = 0; i < 4; i++ )
106 | {
107 | *dest = out[i];
108 | dest++;
109 | }
110 | }
111 | }
112 | *dest = 0;
113 | }
114 |
115 | /*
116 | MODULE NAME: b64.c
117 |
118 | AUTHOR: Bob Trower 08/04/01
119 |
120 | PROJECT: Crypt Data Packaging
121 |
122 | COPYRIGHT: Copyright (c) Trantor Standard Systems Inc., 2001
123 |
124 | NOTE: This source code may be used as you wish, subject to
125 | the MIT license. See the LICENCE section below.
126 |
127 | DESCRIPTION:
128 | This little utility implements the Base64
129 | Content-Transfer-Encoding standard described in
130 | RFC1113 (http://www.faqs.org/rfcs/rfc1113.html).
131 |
132 | This is the coding scheme used by MIME to allow
133 | binary data to be transferred by SMTP mail.
134 |
135 | Groups of 3 bytes from a binary stream are coded as
136 | groups of 4 bytes in a text stream.
137 |
138 | The input stream is 'padded' with zeros to create
139 | an input that is an even multiple of 3.
140 |
141 | A special character ('=') is used to denote padding so
142 | that the stream can be decoded back to its exact size.
143 |
144 | Encoded output is formatted in lines which should
145 | be a maximum of 72 characters to conform to the
146 | specification. This program defaults to 72 characters,
147 | but will allow more or less through the use of a
148 | switch. The program enforces a minimum line size
149 | of 4 characters.
150 |
151 | Example encoding:
152 |
153 | The stream 'ABCD' is 32 bits long. It is mapped as
154 | follows:
155 |
156 | ABCD
157 |
158 | A (65) B (66) C (67) D (68) (None) (None)
159 | 01000001 01000010 01000011 01000100
160 |
161 | 16 (Q) 20 (U) 9 (J) 3 (D) 17 (R) 0 (A) NA (=) NA (=)
162 | 010000 010100 001001 000011 010001 000000 000000 000000
163 |
164 |
165 | QUJDRA==
166 |
167 | Decoding is the process in reverse. A 'decode' lookup
168 | table has been created to avoid string scans.
169 |
170 | DESIGN GOALS: Specifically:
171 | Code is a stand-alone utility to perform base64
172 | encoding/decoding. It should be genuinely useful
173 | when the need arises and it meets a need that is
174 | likely to occur for some users.
175 | Code acts as sample code to show the author's
176 | design and coding style.
177 |
178 | Generally:
179 | This program is designed to survive:
180 | Everything you need is in a single source file.
181 | It compiles cleanly using a vanilla ANSI C compiler.
182 | It does its job correctly with a minimum of fuss.
183 | The code is not overly clever, not overly simplistic
184 | and not overly verbose.
185 | Access is 'cut and paste' from a web page.
186 | Terms of use are reasonable.
187 |
188 | VALIDATION: Non-trivial code is never without errors. This
189 | file likely has some problems, since it has only
190 | been tested by the author. It is expected with most
191 | source code that there is a period of 'burn-in' when
192 | problems are identified and corrected. That being
193 | said, it is possible to have 'reasonably correct'
194 | code by following a regime of unit test that covers
195 | the most likely cases and regression testing prior
196 | to release. This has been done with this code and
197 | it has a good probability of performing as expected.
198 |
199 | Unit Test Cases:
200 |
201 | case 0:empty file:
202 | CASE0.DAT -> ->
203 | (Zero length target file created
204 | on both encode and decode.)
205 |
206 | case 1:One input character:
207 | CASE1.DAT A -> QQ== -> A
208 |
209 | case 2:Two input characters:
210 | CASE2.DAT AB -> QUJD -> AB
211 |
212 | case 3:Three input characters:
213 | CASE3.DAT ABC -> QUJD -> ABC
214 |
215 | case 4:Four input characters:
216 | case4.dat ABCD -> QUJDRA== -> ABCD
217 |
218 | case 5:All chars from 0 to ff, linesize set to 50:
219 |
220 | AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIj
221 | JCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZH
222 | SElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWpr
223 | bG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P
224 | kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKz
225 | tLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX
226 | 2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7
227 | /P3+/w==
228 |
229 | case 6:Mime Block from e-mail:
230 | (Data same as test case 5)
231 |
232 | case 7: Large files:
233 | Tested 28 MB file in/out.
234 |
235 | case 8: Random Binary Integrity:
236 | This binary program (b64.exe) was encoded to base64,
237 | back to binary and then executed.
238 |
239 | case 9 Stress:
240 | All files in a working directory encoded/decoded
241 | and compared with file comparison utility to
242 | ensure that multiple runs do not cause problems
243 | such as exhausting file handles, tmp storage, etc.
244 |
245 | -------------
246 |
247 | Syntax, operation and failure:
248 | All options/switches tested. Performs as
249 | expected.
250 |
251 | case 10:
252 | No Args -- Shows Usage Screen
253 | Return Code 1 (Invalid Syntax)
254 | case 11:
255 | One Arg (invalid) -- Shows Usage Screen
256 | Return Code 1 (Invalid Syntax)
257 | case 12:
258 | One Arg Help (-?) -- Shows detailed Usage Screen.
259 | Return Code 0 (Success -- help request is valid).
260 | case 13:
261 | One Arg Help (-h) -- Shows detailed Usage Screen.
262 | Return Code 0 (Success -- help request is valid).
263 | case 14:
264 | One Arg (valid) -- Uses stdin/stdout (filter)
265 | Return Code 0 (Sucess)
266 | case 15:
267 | Two Args (invalid file) -- shows system error.
268 | Return Code 2 (File Error)
269 | case 16:
270 | Encode non-existent file -- shows system error.
271 | Return Code 2 (File Error)
272 | case 17:
273 | Out of disk space -- shows system error.
274 | Return Code 3 (File I/O Error)
275 |
276 | -------------
277 |
278 | Compile/Regression test:
279 | gcc compiled binary under Cygwin
280 | Microsoft Visual Studio under Windows 2000
281 | Microsoft Version 6.0 C under Windows 2000
282 |
283 | DEPENDENCIES: None
284 |
285 | LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc.
286 |
287 | Permission is hereby granted, free of charge, to any person
288 | obtaining a copy of this software and associated
289 | documentation files (the "Software"), to deal in the
290 | Software without restriction, including without limitation
291 | the rights to use, copy, modify, merge, publish, distribute,
292 | sublicense, and/or sell copies of the Software, and to
293 | permit persons to whom the Software is furnished to do so,
294 | subject to the following conditions:
295 |
296 | The above copyright notice and this permission notice shall
297 | be included in all copies or substantial portions of the
298 | Software.
299 |
300 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
301 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
302 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
303 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
304 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
305 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
306 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
307 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
308 |
309 | VERSION HISTORY:
310 | Bob Trower 08/04/01 -- Create Version 0.00.00B
311 | */
312 |
--------------------------------------------------------------------------------
/common/b64.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef B64_H
17 | #define B64_H 1
18 |
19 | size_t b64_decode(char *outstr, const char *indata, size_t in_len);
20 | void b64_encode( char *dest, char *src, size_t src_len);
21 |
22 | #endif
23 |
24 |
--------------------------------------------------------------------------------
/common/cJSON.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2009 Dave Gamble
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in
12 | all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | THE SOFTWARE.
21 | */
22 |
23 | #ifndef cJSON__h
24 | #define cJSON__h
25 |
26 | #ifdef __cplusplus
27 | extern "C"
28 | {
29 | #endif
30 |
31 | #include
32 |
33 | // cJSON Types:
34 | #define cJSON_False 0
35 | #define cJSON_True 1
36 | #define cJSON_NULL 2
37 | #define cJSON_Number 3
38 | #define cJSON_String 4
39 | #define cJSON_Array 5
40 | #define cJSON_Object 6
41 |
42 | #define cJSON_IsReference 256
43 |
44 | // The cJSON structure:
45 | typedef struct cJSON {
46 | struct cJSON *next,*prev; // next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem
47 | struct cJSON *child; // An array or object item will have a child pointer pointing to a chain of the items in the array/object.
48 |
49 | int type; // The type of the item, as above.
50 |
51 | char *valuestring; // The item's string, if type==cJSON_String
52 | int valueint; // The item's number, if type==cJSON_Number
53 | double valuedouble; // The item's number, if type==cJSON_Number
54 |
55 | char *string; // The item's name string, if this item is the child of, or is in the list of subitems of an object.
56 | } cJSON;
57 |
58 | typedef struct cJSON_Hooks {
59 | void *(*malloc_fn)(size_t sz);
60 | void (*free_fn)(void *ptr);
61 | } cJSON_Hooks;
62 |
63 | // Supply malloc, realloc and free functions to cJSON
64 | extern void cJSON_InitHooks(cJSON_Hooks* hooks);
65 |
66 |
67 | // Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished.
68 | extern cJSON *cJSON_Parse(const char *value);
69 | // Render a cJSON entity to text for transfer/storage. Free the char* when finished.
70 | extern char *cJSON_Print(cJSON *item);
71 | // Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished.
72 | extern char *cJSON_PrintUnformatted(cJSON *item);
73 | // Delete a cJSON entity and all subentities.
74 | extern void cJSON_Delete(cJSON *c);
75 |
76 | // Returns the number of items in an array (or object).
77 | extern int cJSON_GetArraySize(cJSON *array);
78 | // Retrieve item number "item" from array "array". Returns NULL if unsuccessful.
79 | extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
80 | // Get item "string" from object. Case insensitive.
81 | extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
82 |
83 | // These calls create a cJSON item of the appropriate type.
84 | extern cJSON *cJSON_CreateNull();
85 | extern cJSON *cJSON_CreateTrue();
86 | extern cJSON *cJSON_CreateFalse();
87 | extern cJSON *cJSON_CreateNumber(double num);
88 | extern cJSON *cJSON_CreateString(const char *string);
89 | extern cJSON *cJSON_CreateArray();
90 | extern cJSON *cJSON_CreateObject();
91 |
92 | // These utilities create an Array of count items.
93 | extern cJSON *cJSON_CreateIntArray(int *numbers,int count);
94 | extern cJSON *cJSON_CreateFloatArray(float *numbers,int count);
95 | extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count);
96 | extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
97 |
98 | // Append item to the specified array/object.
99 | extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
100 | extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
101 | // Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON.
102 | extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
103 | extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
104 |
105 | // Remove/Detatch items from Arrays/Objects.
106 | extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
107 | extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
108 | extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
109 | extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
110 |
111 | // Update array items.
112 | extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
113 | extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
114 |
115 | #define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
116 | #define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
117 | #define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
118 | #define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
119 | #define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
120 |
121 | #ifdef __cplusplus
122 | }
123 | #endif
124 |
125 | #endif
126 |
--------------------------------------------------------------------------------
/common/common.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef COMMON_H
17 |
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 |
24 | #define TRUE true
25 | #define FALSE false
26 | #define BOOLEAN bool
27 |
28 | #endif
29 |
30 |
--------------------------------------------------------------------------------
/common/jitter.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #include
22 | #include "jitter.h"
23 |
24 | static bool jitter_enabled;
25 | static struct timeval last;
26 | static double last_delay_us = 0;
27 |
28 | void jitter_enable(bool enabled)
29 | {
30 | jitter_enabled = enabled;
31 | }
32 |
33 | void jitter_check(void)
34 | {
35 | double jitter_us;
36 | double delay_us;
37 | struct timeval now;
38 |
39 | if (jitter_enabled)
40 | {
41 | gettimeofday(&now, NULL);
42 |
43 | delay_us = (now.tv_sec - last.tv_sec) * 1000000.0;
44 | delay_us += now.tv_usec - last.tv_usec;
45 |
46 | jitter_us = delay_us - last_delay_us;
47 |
48 | if (jitter_us >= 0)
49 | LOG_USER("jitter: +%5.4fms", jitter_us / 1000.0);
50 | else
51 | LOG_USER("jitter: %5.4fms", jitter_us / 1000.0);
52 |
53 | memcpy(&last, &now, sizeof(struct timeval));
54 | last_delay_us = delay_us;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/common/jitter.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef JITTER_H
17 | #define JITTER_H 1
18 | #include
19 |
20 | void jitter_enable(bool enabled);
21 | void jitter_check(void);
22 | #endif
23 |
24 |
--------------------------------------------------------------------------------
/common/logging.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 |
25 | #include "logging.h"
26 | #include "util.h"
27 |
28 | static int loglevel = LOGLEVEL_INFO;
29 |
30 | void log_init(void)
31 | {
32 | }
33 |
34 | void log_setlevel(int level)
35 | {
36 | loglevel = level;
37 | }
38 |
39 | void log_msg(int level, const char *func, const char *format, ...)
40 | {
41 | va_list args;
42 | const char *timeformat = "%Y-%m-%dT%H:%M:%S";
43 | char timestr[256];
44 | time_t t;
45 |
46 | t = time(NULL);
47 | strftime(timestr, sizeof(timestr), timeformat, gmtime(&t));
48 |
49 |
50 | if (loglevel >= level || level == LOGLEVEL_CRITICAL)
51 | {
52 | if (level == LOGLEVEL_DEBUG)
53 | fprintf(stderr, "[%s()] ", func);
54 | va_start(args, format);
55 | fprintf(stderr, "[%s] ", timestr);
56 | vfprintf(stderr, format, args);
57 | fprintf(stderr, "\r\n");
58 | va_end(args);
59 | }
60 |
61 | if (level == LOGLEVEL_CRITICAL)
62 | exit(1);
63 | }
64 |
65 | void log_msg_noln(int level, const char *func, const char *format, ...)
66 | {
67 | va_list args;
68 |
69 | if (loglevel >= level || loglevel == LOGLEVEL_CRITICAL)
70 | {
71 | if (level == LOGLEVEL_DEBUG)
72 | fprintf(stderr, "[%s()] ", func);
73 | va_start(args, format);
74 | vfprintf(stderr, format, args);
75 | va_end(args);
76 | }
77 |
78 | if (level == LOGLEVEL_CRITICAL)
79 | exit(1);
80 | }
81 |
82 |
--------------------------------------------------------------------------------
/common/logging.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef LOGGING_H
17 | #define LOGGING_H 1
18 |
19 | #include
20 |
21 | #define LOGLEVEL_CRITICAL (-2)
22 | #define LOGLEVEL_USER (-1)
23 | #define LOGLEVEL_ERROR 0
24 | #define LOGLEVEL_WARN 2
25 | #define LOGLEVEL_INFO 3
26 | #define LOGLEVEL_DEBUG 4
27 |
28 | #define LOG_CRITICAL(...) log_msg(LOGLEVEL_CRITICAL, __FUNCTION__, __VA_ARGS__)
29 | #define LOG_USER(...) log_msg(LOGLEVEL_USER, __FUNCTION__, __VA_ARGS__)
30 | #define LOG_ERROR(...) log_msg(LOGLEVEL_ERROR, __FUNCTION__, __VA_ARGS__)
31 | #define LOG_WARN(...) log_msg(LOGLEVEL_WARN, __FUNCTION__, __VA_ARGS__)
32 | #define LOG_INFO(...) log_msg(LOGLEVEL_INFO, __FUNCTION__, __VA_ARGS__)
33 | #define LOG_DEBUG(...) log_msg(LOGLEVEL_DEBUG, __FUNCTION__, __VA_ARGS__)
34 |
35 | #define LOG_USER_NOLN(...) log_msg_noln(LOGLEVEL_USER, __FUNCTION__, __VA_ARGS__)
36 | #define LOG_ERROR_NOLN(...) log_msg_noln(LOGLEVEL_ERROR, __FUNCTION__, __VA_ARGS__)
37 | #define LOG_WARN_NOLN(...) log_msg_noln(LOGLEVEL_WARN, __FUNCTION__, __VA_ARGS__)
38 | #define LOG_INFO_NOLN(...) log_msg_noln(LOGLEVEL_INFO, __FUNCTION__, __VA_ARGS__)
39 | #define LOG_DEBUG_NOLN(...) log_msg_noln(LOGLEVEL_DEBUG, __FUNCTION__, __VA_ARGS__)
40 |
41 | void log_init(void);
42 | void log_setlevel(int level);
43 | void log_msg(int level, const char *func, const char *format, ...);
44 | void log_msg_noln(int level, const char *func, const char *format, ...);
45 |
46 | #endif
47 |
48 |
--------------------------------------------------------------------------------
/common/path.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 |
23 | bool path_in_dir(const char *root, const char *path)
24 | {
25 | char path_full[PATH_MAX];
26 | char root_full[PATH_MAX];
27 |
28 | if (strlen(root) >= PATH_MAX || strlen(path) >= PATH_MAX)
29 | {
30 | LOG_ERROR("too long");
31 | return false;
32 | }
33 | if (NULL == realpath(path, path_full))
34 | {
35 | LOG_DEBUG("bad path '%s'", path);
36 | return false;
37 | }
38 | if (NULL == realpath(root, root_full))
39 | {
40 | LOG_ERROR("bad root");
41 | return false;
42 | }
43 | if (strlen(root_full) > strlen(path_full))
44 | {
45 | LOG_ERROR("too short");
46 | return false;
47 | }
48 | if (0 != strncmp(root_full, path_full, strlen(root_full)))
49 | {
50 | LOG_ERROR("path root mismatch");
51 | return false;
52 | }
53 | return true;
54 | }
55 |
--------------------------------------------------------------------------------
/common/path.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef PATH_H
17 | #define PATH_H 1
18 |
19 | bool path_in_dir(const char *root, const char *path);
20 |
21 | #endif
22 |
23 |
--------------------------------------------------------------------------------
/common/pidfile.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 |
25 | #include "pidfile.h"
26 |
27 | int pidfile_write(const char *filename)
28 | {
29 | FILE *fp;
30 |
31 | fp = fopen(filename, "w");
32 | if (NULL == fp)
33 | return 1;
34 |
35 | fprintf(fp, "%d", getpid());
36 | fclose(fp);
37 |
38 | return 0;
39 | }
40 |
41 |
--------------------------------------------------------------------------------
/common/pidfile.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef PIDFILE_H
17 | #define PIDFILE_H
18 |
19 | int pidfile_write(const char *filename);
20 |
21 | #endif
22 |
23 |
--------------------------------------------------------------------------------
/common/random.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 | #include
30 | #include
31 | #include
32 |
33 | static int random_fd = -1;
34 |
35 | int random_init(void)
36 | {
37 | random_fd = open("/dev/urandom", O_RDONLY);
38 | if (random_fd < 0)
39 | return 1;
40 | return 0;
41 | }
42 |
43 | void random_read(uint8_t *buf, size_t len)
44 | {
45 | #if 1
46 | if (random_fd < 0)
47 | LOG_CRITICAL("random_init failed/not called");
48 |
49 | if (len != read(random_fd, buf, len))
50 | LOG_CRITICAL("random_read failed");
51 | #else
52 | #warning BUST RANDOM
53 | while(len--)
54 | *buf++ = 4;
55 | #endif
56 | }
57 |
58 |
--------------------------------------------------------------------------------
/common/random.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef RANDOM_H
17 | #define RANDOM_H 1
18 |
19 | int random_init(void);
20 | void random_read(uint8_t *buf, size_t len);
21 |
22 | #endif
23 |
24 |
--------------------------------------------------------------------------------
/common/tdate_parse.c:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tobyjaffey/mqtt-http-server/HEAD/common/tdate_parse.c
--------------------------------------------------------------------------------
/common/tdate_parse.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tobyjaffey/mqtt-http-server/HEAD/common/tdate_parse.h
--------------------------------------------------------------------------------
/common/util.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 |
25 | #include
26 | #include
27 | //#include
28 | #include
29 | #include
30 | #include
31 |
32 | #include
33 | #include
34 |
35 | void crash(void)
36 | {
37 | // crash for benefit of debugger
38 | char *p = NULL;
39 | *p = 0;
40 | }
41 |
42 | time_t getTime(void)
43 | {
44 | struct timeval tv;
45 | gettimeofday(&tv, NULL);
46 | return tv.tv_sec;
47 | }
48 |
49 | static uint8_t tolowercase(uint8_t ch)
50 | {
51 | if ((ch >= 'A') && (ch <= 'Z'))
52 | return ch + 0x20; // Convert uppercase to lowercase
53 | return ch; // Simply return original character if it doesn't require any adjustments
54 | }
55 |
56 | static int8_t parseHexDigit(uint8_t digit)
57 | {
58 | digit = tolowercase(digit);
59 | if (isdigit(digit))
60 | return (int8_t)digit - '0';
61 | if ((digit >= 'a') && (digit <= 'f'))
62 | return (int8_t)digit + 0xA - 'a';
63 | return -1; // Error case - input wasn't a valid hex digit
64 | }
65 |
66 | int hexstring_parse(const char *hexstr, uint8_t *buf, size_t *buflen)
67 | {
68 | size_t hexstrlen = strlen(hexstr);
69 | size_t i;
70 |
71 | if (hexstrlen & 0x1)
72 | {
73 | LOG_DEBUG("hexstring_parse: not even");
74 | return 1;
75 | }
76 |
77 | if (*buflen < hexstrlen/2)
78 | {
79 | LOG_DEBUG("hexstring_parse: buffer too small %d < %d", *buflen, hexstrlen/2);
80 | return 1;
81 | }
82 |
83 | for (i=0;i
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef UTIL_H
17 | #define UTIL_H 1
18 |
19 | #include
20 | #include
21 | #include
22 |
23 | void crash(void);
24 |
25 | int hexstring_parse(const char *hexstr, uint8_t *buf, size_t *buflen);
26 | time_t getTime(void);
27 |
28 | #endif
29 |
30 |
--------------------------------------------------------------------------------
/evhelper.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef EV_HELPER_H
17 | #define EV_HELPER_H
18 |
19 | #include
20 |
21 | struct ev_io_helper
22 | {
23 | ev_io io;
24 | void *userdata;
25 | };
26 | typedef struct ev_io_helper ev_io_helper_t;
27 |
28 | struct ev_timer_helper
29 | {
30 | ev_timer timer;
31 | void *userdata;
32 | };
33 | typedef struct ev_timer_helper ev_timer_helper_t;
34 |
35 | #endif
36 |
37 |
--------------------------------------------------------------------------------
/htdocs/about.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MQTT-HTTP-Server
5 |
6 |
7 |
8 |
What is this?
9 |
10 | MQTT is a lightweight publish subscribe protocol. This server provides an HTTP bridge to an MQTT broker. It uses long-polling to provide streaming data to browsers.
11 |
12 |
17 | Bridging MQTT onto the web allows for browser based apps, mashups and experiments with real-time data.
18 |
19 | MQTT and HTTP are very different protocols. Read more about my take on the problem here.
20 |
21 | For browsers which support WebSockets, http://test.mosquitto.org/ws.html provides similar functionality
22 |
23 |
17 | This page shows a simple example of subscribing and publishing to MQTT topics. Clicking the buttons publishes. The checkboxes are subscribed to topics.
18 |
19 |
20 |
21 | sensemote/devices/button1
22 |
23 |
24 |
25 |
26 |
27 |
28 | sensemote/devices/button2
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | The checkboxes can be controlled from any MQTT client, eg.
39 |
55 | This page can be opened by multiple clients and used to share data via the MQTT broker.
56 |
57 |
58 |
59 |
60 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/httpd.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef HTTPD_H
17 | #define HTTPD_H 1
18 |
19 | #include
20 | #include "ebb.h"
21 | #include "idset.h"
22 | #include "tailq.h"
23 |
24 | typedef int (*query_iterate_func)(const char *key, const char *val, void *userdata);
25 |
26 | #define MAX_VHOST 32
27 |
28 | typedef struct
29 | {
30 | char *rootdir;
31 | char *hostname;
32 | char *name;
33 | } httpd_virt;
34 |
35 | typedef struct
36 | {
37 | ebb_connection *conn;
38 | uint32_t connid;
39 | char *body; // request body
40 | size_t body_len;
41 | char *uri; // request uri (including query str)
42 | char *path; // request path part of uri
43 | char *auth;
44 | char host[512];
45 | bool parsing_auth_header;
46 | bool parsing_ifmodifiedsince_header;
47 | bool parsing_host_header;
48 | } ebb_request_info;
49 |
50 | typedef struct
51 | {
52 | ebb_connection *conn;
53 | uint32_t connid; // unique id for this connection
54 | bool keepalive; // should this connection be kept alive after request (>= HTTP1.1)
55 | tailq_t *rsp_q;
56 | bool writing; // busy writing an item from rsp_q
57 | bool finished; // user sets this to say, after flushing queue, hangup.
58 | size_t push_len; // total bytes pending
59 |
60 | bool have_mid;
61 | int mid;
62 |
63 | int version_major;
64 | int version_minor;
65 |
66 | bool reset;
67 |
68 | char writebuf[16384];
69 | char mimetype[512];
70 | int http_status;
71 | char *errorbody;
72 | time_t ifmodifiedsince;
73 | char last_modified[512];
74 |
75 | char *managed_str; // userdata, but owned by this struct and freed by destructor
76 |
77 | idset_t *streamids; // set of streams associated with this connection
78 | bool rawmode; // immediate response wanted
79 |
80 | char ipaddr[32];
81 |
82 | } ebb_connection_info;
83 |
84 | extern int httpd_init(ebb_server *server, struct ev_loop *loop);
85 | extern int httpd_listen(ebb_server *server, const char *rootdir, uint16_t port, const char *cert, const char *key);
86 | extern int httpd_push(uint32_t connid, const char *msg, bool flush);
87 | extern int httpd_pushbin(uint32_t connid, const uint8_t *data, size_t len, bool flush);
88 | extern int httpd_close(uint32_t connid);
89 | extern void httpd_open_cb(uint32_t connid);
90 | extern void httpd_close_cb(uint32_t connid);
91 | extern int httpd_request_cb(uint32_t connid, const char *host, uint32_t method, const char *uri, const char *path, const char *body_data, size_t body_len, const char *auth);
92 | extern int httpd_flush_stale(uint32_t connid);
93 | extern int httpd_printf(uint32_t connid, const char *format, ...);
94 | extern int httpd_query_iterate(const char *uri_str, query_iterate_func f, void *userdata);
95 | extern ebb_connection_info *httpd_get_conninfo(uint32_t connid);
96 | extern void httpd_timeout_conn(uint32_t connid);
97 | extern void httpd_request_complete(uint32_t connid, ebb_connection_info *conninfo);
98 |
99 | extern int httpd_addvirt(ebb_server *server, const char *rootdir, const char *hostname, const char *name);
100 | extern int httpd_lookupvirt(const char *host);
101 |
102 | #endif
103 |
104 |
--------------------------------------------------------------------------------
/httpdconnlist.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include "httpd.h"
19 | #include "httpdconnlist.h"
20 | #include
21 |
22 | struct httpdconn_node
23 | {
24 | RB_ENTRY(httpdconn_node) entry;
25 | uint32_t connid;
26 | ebb_connection *conn;
27 | };
28 |
29 | static int httpdconn_cmp(struct httpdconn_node *e1, struct httpdconn_node *e2)
30 | {
31 | return (e1->connid < e2->connid ? -1 : e1->connid > e2->connid);
32 | }
33 |
34 | RB_HEAD(httpdconnlist, httpdconn_node) httpdconn_head = RB_INITIALIZER(&httpdconn_head);
35 | RB_PROTOTYPE(httpdconnlist, httpdconn_node, entry, httpdconn_cmp);
36 | RB_GENERATE(httpdconnlist, httpdconn_node, entry, httpdconn_cmp);
37 |
38 | int httpdconnlist_insert(uint32_t connid, ebb_connection *conn)
39 | {
40 | struct httpdconn_node *data;
41 |
42 | if (NULL == (data = malloc(sizeof(struct httpdconn_node))))
43 | return 1;
44 |
45 | data->connid = connid;
46 | data->conn = conn;
47 |
48 | if (NULL == RB_INSERT(httpdconnlist, &httpdconn_head, data)) // NULL means OK
49 | {
50 | return 0;
51 | }
52 | else
53 | {
54 | LOG_CRITICAL("httpdconnlist_insert connid collision"); // can't happen
55 | return 1;
56 | }
57 |
58 | }
59 |
60 | int httpdconnlist_remove(uint32_t connid)
61 | {
62 | struct httpdconn_node key;
63 | struct httpdconn_node *res;
64 | key.connid = connid;
65 | res = RB_FIND(httpdconnlist, &httpdconn_head, &key);
66 | if (NULL == res)
67 | return 1;
68 | RB_REMOVE(httpdconnlist, &httpdconn_head, res);
69 | free(res);
70 | return 0;
71 | }
72 |
73 | struct ebb_connection *httpdconnlist_find(uint32_t connid)
74 | {
75 | struct httpdconn_node key;
76 | struct httpdconn_node *res;
77 | key.connid = connid;
78 | res = RB_FIND(httpdconnlist, &httpdconn_head, &key);
79 | if (NULL == res)
80 | return NULL;
81 | return res->conn;
82 | }
83 |
84 | void httpdconnlist_foreach(httpdconnlist_iterate_func f, void *userdata)
85 | {
86 | struct httpdconn_node *i;
87 | RB_FOREACH(i, httpdconnlist, &httpdconn_head)
88 | {
89 | f(i->connid, i->conn, userdata);
90 | }
91 | }
92 |
93 |
94 |
--------------------------------------------------------------------------------
/httpdconnlist.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef HTTPDCONNLIST_H
17 | #define HTTPDCONNLIST_H 1
18 |
19 | #include "httpd.h"
20 |
21 | typedef void(*httpdconnlist_iterate_func)(uint32_t connid, ebb_connection *conn, void *userdata);
22 |
23 | extern int httpdconnlist_insert(uint32_t connid, ebb_connection *conn);
24 | extern int httpdconnlist_remove(uint32_t connid);
25 | extern struct ebb_connection *httpdconnlist_find(uint32_t connid);
26 | extern void httpdconnlist_foreach(httpdconnlist_iterate_func f, void *data);
27 |
28 | #endif
29 |
30 |
--------------------------------------------------------------------------------
/idset.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include
19 | #include "idset.h"
20 | #include
21 |
22 | static int id_cmp(struct idset_node *e1, struct idset_node *e2)
23 | {
24 | return (e1->id < e2->id ? -1 : e1->id > e2->id);
25 | }
26 |
27 | RB_GENERATE(idset, idset_node, entry, id_cmp);
28 |
29 | void idset_destroy(idset_t *ids)
30 | {
31 | if (NULL != ids)
32 | {
33 | struct idset_node *var;
34 | struct idset_node *nxt;
35 |
36 | for (var = RB_MIN(idset, &ids->root); var != NULL; var = nxt)
37 | {
38 | nxt = RB_NEXT(idset, &ids->root, var);
39 | RB_REMOVE(idset, &ids->root, var);
40 | free(var);
41 | }
42 |
43 | free(ids);
44 | }
45 | }
46 |
47 | idset_t *idset_create(void)
48 | {
49 | idset_t *ids;
50 |
51 | if (NULL == (ids = (idset_t *)calloc(sizeof(idset_t), 1)))
52 | goto fail;
53 |
54 | RB_INIT(&ids->root);
55 |
56 | return ids;
57 | fail:
58 | idset_destroy(ids);
59 | return NULL;
60 | }
61 |
62 | int idset_insert(idset_t *ids, uint32_t id)
63 | {
64 | struct idset_node *data;
65 |
66 | if (NULL == (data = malloc(sizeof(struct idset_node))))
67 | return 1;
68 |
69 | data->id = id;
70 |
71 | if (NULL == RB_INSERT(idset, &ids->root, data)) // NULL means OK
72 | {
73 | ids->count++;
74 | return 0;
75 | }
76 | else
77 | {
78 | LOG_ERROR("idset_insert id collision");
79 | crash();
80 | free(data);
81 | return 1;
82 | }
83 | }
84 |
85 | int idset_remove(idset_t *ids, uint32_t id)
86 | {
87 | struct idset_node key;
88 | struct idset_node *res;
89 |
90 | key.id = id;
91 | res = RB_FIND(idset, &ids->root, &key);
92 | if (NULL == res)
93 | return 1;
94 | RB_REMOVE(idset, &ids->root, res);
95 | free(res);
96 | ids->count--;
97 | return 0;
98 | }
99 |
100 | bool idset_find(idset_t *ids, uint32_t id)
101 | {
102 | struct idset_node key;
103 | struct idset_node *res;
104 |
105 | key.id = id;
106 | res = RB_FIND(idset, &ids->root, &key);
107 | if (NULL == res)
108 | return false;
109 | return true;
110 | }
111 |
112 | void idset_foreach(idset_t *ids, idset_iterate_func f, void *userdata)
113 | {
114 | struct idset_node *var, *nxt;
115 | // can't use foreach if we want to allow deletion
116 | for (var = RB_MIN(idset, &ids->root); var != NULL; var = nxt)
117 | {
118 | nxt = RB_NEXT(idset, &ids->root, var);
119 | if (f(ids, var->id, userdata))
120 | {
121 | RB_REMOVE(idset, &ids->root, var);
122 | free(var);
123 | }
124 | }
125 | }
126 |
127 |
128 | void idset_print(idset_t *ids)
129 | {
130 | struct idset_node *i;
131 | RB_FOREACH(i, idset, &ids->root)
132 | {
133 | LOG_INFO("%u", i->id);
134 | }
135 | }
136 |
137 |
138 | size_t idset_size(idset_t *ids)
139 | {
140 | return ids->count;
141 | }
142 |
143 |
--------------------------------------------------------------------------------
/idset.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef IDSET_H
17 | #define IDSET_H 1
18 |
19 | #include
20 |
21 | struct idset_node
22 | {
23 | RB_ENTRY(idset_node) entry;
24 | uint32_t id;
25 | };
26 |
27 | typedef struct
28 | {
29 | RB_HEAD(idset, idset_node) root;
30 | size_t count;
31 | } idset_t;
32 |
33 | RB_PROTOTYPE(idset, idset_node, entry, id_cmp);
34 |
35 | typedef bool(*idset_iterate_func)(idset_t *ids, uint32_t id, void *userdata);
36 |
37 | extern idset_t *idset_create(void);
38 | extern void idset_destroy(idset_t *ids);
39 | extern int idset_insert(idset_t *ids, uint32_t id);
40 | extern int idset_remove(idset_t *ids, uint32_t id);
41 | extern bool idset_find(idset_t *ids, uint32_t id);
42 | extern void idset_foreach(idset_t *ids, idset_iterate_func f, void *data);
43 | extern size_t idset_size(idset_t *ids);
44 | extern void idset_print(idset_t *ids);
45 |
46 | #endif
47 |
48 |
--------------------------------------------------------------------------------
/libebb/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | examples/hello_world
3 | test_request_parser
4 | ebb_request_parser.c
5 | tags
6 |
--------------------------------------------------------------------------------
/libebb/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2008 Ryan Dahl (ry@ndahl.us)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
22 |
--------------------------------------------------------------------------------
/libebb/Makefile:
--------------------------------------------------------------------------------
1 | include config.mk
2 |
3 | DEP = ebb.h ebb_request_parser.h rbtree.h
4 | SRC = ebb.c ebb_request_parser.c rbtree.c
5 | OBJ = ${SRC:.c=.o}
6 |
7 | VERSION = 0.1
8 | NAME=libebb
9 | OUTPUT_LIB=$(NAME).$(SUFFIX).$(VERSION)
10 | OUTPUT_A=$(NAME).a
11 |
12 | LINKER=$(CC) $(LDOPT)
13 |
14 | all: $(OUTPUT_A)
15 |
16 | $(OUTPUT_LIB): $(OBJ)
17 | @echo LINK $@
18 | $(LINKER) -o $(OUTPUT_LIB) $(OBJ) $(SONAME) $(LIBS)
19 |
20 | $(OUTPUT_A): $(OBJ)
21 | @echo AR $@
22 | $(AR) cru $(OUTPUT_A) $(OBJ)
23 | @echo RANLIB $@
24 | $(RANLIB) $(OUTPUT_A)
25 |
26 | .c.o:
27 | ${CC} -c ${CFLAGS} $<
28 |
29 | ${OBJ}: ${DEP}
30 |
31 | ebb_request_parser.c: ebb_request_parser.rl
32 | @echo RAGEL $<
33 | ragel -s -G2 $< -o $@
34 |
35 | test: test_request_parser test_rbtree
36 | time ./test_request_parser
37 | ./test_rbtree
38 |
39 | test_rbtree: test_rbtree.o $(OUTPUT_A)
40 | @echo BUILDING test_rbtree
41 | @$(CC) $(CFLAGS) -o $@ $< $(OUTPUT_A)
42 |
43 | test_request_parser: test_request_parser.o $(OUTPUT_A)
44 | @echo BUILDING test_request_parser
45 | @$(CC) $(CFLAGS) -o $@ $< $(OUTPUT_A)
46 |
47 | examples: examples/hello_world
48 |
49 | examples/hello_world: examples/hello_world.c $(OUTPUT_A)
50 | @echo BUILDING examples/hello_world
51 | @$(CC) -I. $(LIBS) $(CFLAGS) -lev -o $@ $^
52 |
53 | clean:
54 | @echo CLEANING
55 | @rm -f ${OBJ} $(OUTPUT_A) $(OUTPUT_LIB) libebb-${VERSION}.tar.gz
56 | @rm -f test_rbtree test_request_parser
57 | @rm -f examples/hello_world examples/hello_world.o
58 |
59 | clobber: clean
60 | @echo CLOBBERING
61 | @rm -f ebb_request_parser.c
62 |
63 | dist: clean $(SRC)
64 | @echo CREATING dist tarball
65 | @mkdir -p ${NAME}-${VERSION}
66 | @cp -R doc examples LICENSE Makefile README config.mk \
67 | ebb_request_parser.rl ${SRC} ${DEP} ${NAME}-${VERSION}
68 | @tar -cf ${NAME}-${VERSION}.tar ${NAME}-${VERSION}
69 | @gzip ${NAME}-${VERSION}.tar
70 | @rm -rf ${NAME}-${VERSION}
71 |
72 | install: $(OUTPUT_LIB) $(OUTPUT_A)
73 | @echo INSTALLING ${OUTPUT_A} and ${OUTPUT_LIB} to ${PREFIX}/lib
74 | install -d -m755 ${PREFIX}/lib
75 | install -d -m755 ${PREFIX}/include
76 | install -m644 ${OUTPUT_A} ${PREFIX}/lib
77 | install -m755 ${OUTPUT_LIB} ${PREFIX}/lib
78 | ln -sfn $(PREFIX)/lib/$(OUTPUT_LIB) $(PREFIX)/lib/$(NAME).so
79 | @echo INSTALLING headers to ${PREFIX}/include
80 | install -m644 ebb.h ebb_request_parser.h ${PREFIX}/include
81 |
82 | uninstall:
83 | @echo REMOVING so from ${PREFIX}/lib
84 | rm -f ${PREFIX}/lib/${NAME}.*
85 | @echo REMOVING headers from ${PREFIX}/include
86 | rm -f ${PREFIX}/include/ebb.h
87 | rm -f ${PREFIX}/include/ebb_request_parser.h
88 |
89 | upload_website:
90 | scp -r doc/index.html doc/icon.png rydahl@tinyclouds.org:~/web/public/libebb
91 |
92 | .PHONY: all options clean clobber dist install uninstall test examples upload_website
93 |
--------------------------------------------------------------------------------
/libebb/README:
--------------------------------------------------------------------------------
1 | see doc/index.html and examples/hello_world.c for explanation
2 |
3 | webpage: http://tinyclouds.org/libebb/
4 | git repository: http://github.com/ry/libebb/tree/master
5 |
6 | To build libebb please edit config.mk to reflect your system's parameters.
7 |
8 |
--------------------------------------------------------------------------------
/libebb/config.mk:
--------------------------------------------------------------------------------
1 | # libev
2 | EVINC = ../libev
3 | EVLIB = ../libev/.libs
4 | EVLIBS = -L${EVLIB} -lev
5 |
6 | # GnuTLS, comment if you don't want it (necessary for HTTPS)
7 | GNUTLSLIB = /usr/lib
8 | GNUTLSINC = /usr/include
9 | GNUTLSLIBS = -L${GNUTLSLIB} -lgnutls
10 | GNUTLSFLAGS = -DHAVE_GNUTLS
11 | INCS += -I${GNUTLSINC}
12 |
13 | # includes and libs
14 | INCS += -I${EVINC}
15 | LIBS = ${EVLIBS} ${GNUTLSLIBS}
16 |
17 | # flags
18 | CPPFLAGS = -DVERSION=\"$(VERSION)\" ${GNUTLSFLAGS}
19 | CFLAGS += -O2 -g -Werror -Wall ${INCS} ${CPPFLAGS} -fPIC -DEBB_DEFAULT_TIMEOUT=30.0
20 |
21 | LDFLAGS = -s ${LIBS}
22 | LDOPT = -shared
23 | SUFFIX = so
24 | SONAME = -Wl,-soname,$(OUTPUT_LIB)
25 |
26 | # Solaris
27 | #CFLAGS = -fast ${INCS} -DVERSION=\"$(VERSION)\" -fPIC
28 | #LDFLAGS = ${LIBS}
29 | #SONAME =
30 |
31 | # Darwin
32 | # LDOPT = -dynamiclib
33 | # SUFFIX = dylib
34 | # SONAME = -current_version $(VERSION) -compatibility_version $(VERSION)
35 |
36 | # compiler and linker
37 | CC = cc
38 | RANLIB = ranlib
39 |
--------------------------------------------------------------------------------
/libebb/doc/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tobyjaffey/mqtt-http-server/HEAD/libebb/doc/icon.png
--------------------------------------------------------------------------------
/libebb/doc/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
41 |
42 |
43 |
libebb
44 |
45 |
46 | libebb is a lightweight HTTP server library for C. It lays the
47 | foundation for writing a web server by providing the socket juggling
48 | and request parsing. By implementing the HTTP/1.1 grammar provided in
49 | RFC2612, libebb understands most most valid HTTP/1.1 connections
50 | (persistent, pipelined, and chunked requests included) and rejects
51 | invalid or malicious requests. libebb supports SSL over HTTP.
52 |
53 |
54 |
55 | The library embraces a minimalistic single-threaded evented design.
56 | No control is removed from the user. For example, all allocations are
57 | done through callbacks so that the user might implement in optimal
58 | ways for their specific application. By design libebb is not
59 | thread-safe and all provided callbacks must not block. libebb uses
60 | the high-performance
61 | libev event loop, but does not control it. The user of the library may
62 | start and stop the loop at will, they may attach thier own watchers.
63 |
64 |
65 |
66 | libebb depends on POSIX sockets, libev, and optionally GnuTLS.
67 |
68 |
69 |
70 | libebb is in the early stages of development and probably contains
71 | many bugs. The API is subject to radical changes. If you're
72 | interested checkout
73 | the source code and join the mailing
75 | list. A release will be made when it proves stable.
76 |
85 | libebb is a simple API, mostly it is providing callbacks. There are
86 | two types of callbacks that one will work with:
87 |
88 |
89 |
90 |
callbacks to allocate and initialize data for libebb. These are
91 | named new_* like new_connection and
92 | new_request
93 |
callbacks which happen on an event and might provide a pointer to
94 | a chunk of data. These are named on_* like
95 | on_body and on_close.
96 |
97 |
98 |
99 | In libebb there are three important classes: ebb_server,
100 | ebb_connection, and ebb_request.
101 | Each server has many peer connections. Each peer connection may have many
102 | requests.
103 | There are two additional classes ebb_buf and ebb_request_parser
104 | which may or may not be useful.
105 |
106 |
107 |
ebb_server
108 |
109 | ebb_server represents a single web server listening on a
110 | single port. The user must allocate the structure themselves, then
111 | call ebb_server_init() and provide a libev event loop.
112 | ebb_server_set_secure() will make the server understand
113 | HTTPS connections.
114 |
115 |
116 |
117 | After initialized the ebb_server_listen_on_port() can be
118 | called to open the server up to new connections. libebb does not
119 | control the event loop, it is the user's responsibility to start the
120 | event loop (using ev_loop()) after
121 | ebb_server_listen_on_port() is called.
122 |
123 |
124 |
125 | To accept connections you must provide the new server with a callback
126 | called new_connection. This callback must return an allocated
127 | and initialized ebb_connection structure.
128 | To set this callback do
129 |
134 | Additional documentation can be found in ebb.h
135 |
136 |
137 |
ebb_connection
138 |
139 |
140 | This structure contains information and callbacks for a single client
141 | connection. It is allocated and initialized through the
142 | new_connection callback in ebb_server.
143 | To initialize a newly allocated ebb_connection use
144 | ebb_connection_init().
145 |
146 |
147 |
148 | After ebb_connection_init() is called a number of
149 | callbacks can be set: new_request, new_buf,
150 | on_timeout, and on_close.
151 |
152 |
153 |
154 | When an ebb_connection is returned to an
155 | ebb_server, data is immediately data is read from the
156 | socket. This data must be stored somewhere. Because libebb is
157 | agnostic about allocation decisions, it passes this off to the user in
158 | the form of a callback: connection->new_buf. This
159 | callback returns a newly allocated and initialized
160 | ebb_buf structure. How much libebb attempts to read from
161 | the socket is determined by how large the returned
162 | ebb_buf structure is. Using new_buf is
163 | optional. By default libebb reads data into a static buffer
164 | (allocated at compile time), writing over it on each read. In many
165 | web server using the static buffer will be sufficent because callbacks
166 | made during the parsing will buffer the data elsewhere. Providing a
167 | new_buf callback is necessary only if you want to save
168 | the raw data coming from the socket.
169 |
170 |
171 |
172 | The new_request callback is called at the beginning of a
173 | request. It must return a newly allocated and initialized
174 | ebb_request structure. Because HTTP/1.1 supports peristant
176 | connections, there may be many requests per connection.
177 |
178 |
179 |
180 | You may access the file descriptor for the client socket inside the
181 | ebb_connection structure. Writing the response, in valid
182 | HTTP, is the user's responsibility. Remember, requests must be
183 | returned to client in the same order that they were received.
184 |
185 |
186 |
187 | A convience function, ebb_connection_write, is provided
188 | which will write a single string to the peer. You may use
189 | this function or you may write to the file descriptor directly.
190 |
191 |
192 |
193 | To close a peer connection use
194 | ebb_connnection_schedule_close(). Because SSL may require
195 | some additional communication to close the connection properly, the
196 | file descriptor cannot be closed immediately. The
197 | on_close callback will be made when the peer socket is
198 | finally closed.
199 | Only once on_close is called may the
200 | user free the ebb_connection structure.
201 |
202 |
203 |
204 |
ebb_request
205 |
206 |
207 | This structure provides information about a request. For example,
208 | request->method == EBB_POST would mean the method of
209 | the request is POST. There are also many callbacks
210 | which can be set to handle data from a request as it is parsed.
211 |
212 |
213 |
214 | The on_uri callback and all other
215 | ebb_element_cb callbacks provide pointers to request
216 | data. The annoying thing is they do not necessarily provide a
217 | complete string. This is because the reads from the socket may not
218 | contain an entire request and for efficency libebb does not attempt to
219 | buffer the data. Theoretically, one might receive an
220 | on_uri callback 10 times, each providing just a single
221 | character of the request's URI. See ebb_request_parser.h for
222 | a full list of callbacks that you may provide. (If you don't set them,
223 | they are ignored.)
224 |
225 |
226 |
227 | The on_complete callback is called at the end of
228 | each request.
229 | Only once on_complete is called may the
230 | user free the ebb_request structure.
231 |
232 |
233 |
Example
234 |
235 |
236 | A simple example is provided in examples/hello_world.c.
237 |
238 |
239 |
240 |
241 |
--------------------------------------------------------------------------------
/libebb/ebb.h:
--------------------------------------------------------------------------------
1 | /* This file is part of libebb.
2 | *
3 | * Copyright (c) 2008 Ryan Dahl (ry@ndahl.us)
4 | * All rights reserved.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining
7 | * a copy of this software and associated documentation files (the
8 | * "Software"), to deal in the Software without restriction, including
9 | * without limitation the rights to use, copy, modify, merge, publish,
10 | * distribute, sublicense, and/or sell copies of the Software, and to
11 | * permit persons to whom the Software is furnished to do so, subject to
12 | * the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be
15 | * included in all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 | */
25 | #ifndef EBB_H
26 | #define EBB_H
27 |
28 | #include
29 | #include
30 | #include
31 | #ifdef HAVE_GNUTLS
32 | # include
33 | # include "rbtree.h" /* for ebb_server.session_cache */
34 | #endif
35 | #include "ebb_request_parser.h"
36 |
37 | #define EBB_MAX_CONNECTIONS 1024
38 | // #define EBB_DEFAULT_TIMEOUT 30.0
39 |
40 | #define EBB_AGAIN 0
41 | #define EBB_STOP 1
42 |
43 | typedef struct ebb_server ebb_server;
44 | typedef struct ebb_connection ebb_connection;
45 | typedef void (*ebb_after_write_cb) (ebb_connection *connection);
46 | typedef void (*ebb_connection_cb)(ebb_connection *connection, void *data);
47 |
48 | struct ebb_server {
49 | int fd; /* ro */
50 | struct sockaddr_in sockaddr; /* ro */
51 | socklen_t socklen; /* ro */
52 | char port[6]; /* ro */
53 | struct ev_loop *loop; /* ro */
54 | unsigned listening:1; /* ro */
55 | unsigned secure:1; /* ro */
56 | #ifdef HAVE_GNUTLS
57 | gnutls_certificate_credentials_t credentials; /* private */
58 | struct rbtree_t session_cache; /* private */
59 | #endif
60 | ev_io connection_watcher; /* private */
61 |
62 | /* Public */
63 |
64 | /* Allocates and initializes an ebb_connection. NULL by default. */
65 | ebb_connection* (*new_connection) (ebb_server*, struct sockaddr_in*);
66 |
67 | void *data;
68 | };
69 |
70 | struct ebb_connection {
71 | int fd; /* ro */
72 | struct sockaddr_in sockaddr; /* ro */
73 | socklen_t socklen; /* ro */
74 | ebb_server *server; /* ro */
75 | char *ip; /* ro */
76 | unsigned open:1; /* ro */
77 |
78 | const char *to_write; /* ro */
79 | size_t to_write_len; /* ro */
80 | size_t written; /* ro */
81 | ebb_after_write_cb after_write_cb; /* ro */
82 |
83 | ebb_request_parser parser; /* private */
84 | ev_io write_watcher; /* private */
85 | ev_io read_watcher; /* private */
86 | ev_timer timeout_watcher; /* private */
87 | ev_timer goodbye_watcher; /* private */
88 | #ifdef HAVE_GNUTLS
89 | ev_io handshake_watcher; /* private */
90 | gnutls_session_t session; /* private */
91 | ev_io goodbye_tls_watcher; /* private */
92 | #endif
93 |
94 | /* Public */
95 |
96 | ebb_request* (*new_request) (ebb_connection*);
97 |
98 | /* Returns EBB_STOP or EBB_AGAIN. NULL by default. */
99 | int (*on_timeout) (ebb_connection*);
100 |
101 | void (*on_close) (ebb_connection*);
102 |
103 | void *data;
104 | };
105 |
106 | void ebb_server_init (ebb_server *server, struct ev_loop *loop);
107 | #ifdef HAVE_GNUTLS
108 | int ebb_server_set_secure (ebb_server *server, const char *cert_file,
109 | const char *key_file);
110 | #endif
111 | int ebb_server_listen_on_port (ebb_server *server, const int port);
112 | int ebb_server_listen_on_fd (ebb_server *server, const int sfd);
113 | void ebb_server_unlisten (ebb_server *server);
114 |
115 | void ebb_connection_init (ebb_connection *);
116 | void ebb_connection_schedule_close (ebb_connection *);
117 | void ebb_connection_reset_timeout (ebb_connection *);
118 | int ebb_connection_write (ebb_connection *, const char *buf, size_t len, ebb_after_write_cb);
119 |
120 | #endif
121 |
--------------------------------------------------------------------------------
/libebb/ebb_request_parser.h:
--------------------------------------------------------------------------------
1 | /* This file is part of the libebb web server library
2 | *
3 | * Copyright (c) 2008 Ryan Dahl (ry@ndahl.us)
4 | * All rights reserved.
5 | *
6 | * This parser is based on code from Zed Shaw's Mongrel.
7 | * Copyright (c) 2005 Zed A. Shaw
8 | *
9 | * Permission is hereby granted, free of charge, to any person obtaining
10 | * a copy of this software and associated documentation files (the
11 | * "Software"), to deal in the Software without restriction, including
12 | * without limitation the rights to use, copy, modify, merge, publish,
13 | * distribute, sublicense, and/or sell copies of the Software, and to
14 | * permit persons to whom the Software is furnished to do so, subject to
15 | * the following conditions:
16 | *
17 | * The above copyright notice and this permission notice shall be
18 | * included in all copies or substantial portions of the Software.
19 | *
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 | */
28 | #ifndef ebb_request_parser_h
29 | #define ebb_request_parser_h
30 | #ifdef __cplusplus
31 | extern "C" {
32 | #endif
33 |
34 |
35 | #include
36 |
37 | typedef struct ebb_request ebb_request;
38 | typedef struct ebb_request_parser ebb_request_parser;
39 | typedef void (*ebb_header_cb)(ebb_request*, const char *at, size_t length, int header_index);
40 | typedef void (*ebb_element_cb)(ebb_request*, const char *at, size_t length);
41 |
42 | #define EBB_MAX_MULTIPART_BOUNDARY_LEN 20
43 |
44 | /* HTTP Methods */
45 | #define EBB_COPY 0x00000001
46 | #define EBB_DELETE 0x00000002
47 | #define EBB_GET 0x00000004
48 | #define EBB_HEAD 0x00000008
49 | #define EBB_LOCK 0x00000010
50 | #define EBB_MKCOL 0x00000020
51 | #define EBB_MOVE 0x00000040
52 | #define EBB_OPTIONS 0x00000080
53 | #define EBB_POST 0x00000100
54 | #define EBB_PROPFIND 0x00000200
55 | #define EBB_PROPPATCH 0x00000400
56 | #define EBB_PUT 0x00000800
57 | #define EBB_TRACE 0x00001000
58 | #define EBB_UNLOCK 0x00002000
59 |
60 | /* Transfer Encodings */
61 | #define EBB_IDENTITY 0x00000001
62 | #define EBB_CHUNKED 0x00000002
63 |
64 | struct ebb_request {
65 | int method;
66 | int transfer_encoding; /* ro */
67 | int expect_continue; /* ro */
68 | unsigned int version_major; /* ro */
69 | unsigned int version_minor; /* ro */
70 | int number_of_headers; /* ro */
71 | int keep_alive; /* private - use ebb_request_should_keep_alive */
72 | size_t content_length; /* ro - 0 if unknown */
73 | size_t body_read; /* ro */
74 |
75 | /* Public - ordered list of callbacks */
76 | ebb_element_cb on_path;
77 | ebb_element_cb on_query_string;
78 | ebb_element_cb on_uri;
79 | ebb_element_cb on_fragment;
80 | ebb_header_cb on_header_field;
81 | ebb_header_cb on_header_value;
82 | void (*on_headers_complete)(ebb_request *);
83 | ebb_element_cb on_body;
84 | void (*on_complete)(ebb_request *);
85 | void *data;
86 | };
87 |
88 | struct ebb_request_parser {
89 | int cs; /* private */
90 | size_t chunk_size; /* private */
91 | unsigned eating:1; /* private */
92 | ebb_request *current_request; /* ro */
93 | const char *header_field_mark;
94 | const char *header_value_mark;
95 | const char *query_string_mark;
96 | const char *path_mark;
97 | const char *uri_mark;
98 | const char *fragment_mark;
99 |
100 | /* Public */
101 | ebb_request* (*new_request)(void*);
102 | void *data;
103 | };
104 |
105 | void ebb_request_parser_init(ebb_request_parser *parser);
106 | size_t ebb_request_parser_execute(ebb_request_parser *parser, const char *data, size_t len);
107 | int ebb_request_parser_has_error(ebb_request_parser *parser);
108 | int ebb_request_parser_is_finished(ebb_request_parser *parser);
109 | void ebb_request_init(ebb_request *);
110 | int ebb_request_should_keep_alive(ebb_request *request);
111 | #define ebb_request_has_body(request) \
112 | (request->transfer_encoding == EBB_CHUNKED || request->content_length > 0 )
113 |
114 | #ifdef __cplusplus
115 | }
116 | #endif
117 | #endif
118 |
--------------------------------------------------------------------------------
/libebb/ebb_request_parser.rl:
--------------------------------------------------------------------------------
1 | /* This file is part of the libebb web server library
2 | *
3 | * Copyright (c) 2008 Ryan Dahl (ry@ndahl.us)
4 | * All rights reserved.
5 | *
6 | * This parser is based on code from Zed Shaw's Mongrel.
7 | * Copyright (c) 2005 Zed A. Shaw
8 | *
9 | * Permission is hereby granted, free of charge, to any person obtaining
10 | * a copy of this software and associated documentation files (the
11 | * "Software"), to deal in the Software without restriction, including
12 | * without limitation the rights to use, copy, modify, merge, publish,
13 | * distribute, sublicense, and/or sell copies of the Software, and to
14 | * permit persons to whom the Software is furnished to do so, subject to
15 | * the following conditions:
16 | *
17 | * The above copyright notice and this permission notice shall be
18 | * included in all copies or substantial portions of the Software.
19 | *
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 | */
28 | #include "ebb_request_parser.h"
29 |
30 | #include
31 | #include
32 |
33 | static int unhex[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
34 | ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
35 | ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
36 | , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
37 | ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
38 | ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
39 | ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
40 | ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
41 | };
42 | #define TRUE 1
43 | #define FALSE 0
44 | #define MIN(a,b) (a < b ? a : b)
45 |
46 | #define REMAINING (pe - p)
47 | #define CURRENT (parser->current_request)
48 | #define CONTENT_LENGTH (parser->current_request->content_length)
49 | #define CALLBACK(FOR) \
50 | if(CURRENT && parser->FOR##_mark && CURRENT->on_##FOR) { \
51 | CURRENT->on_##FOR( CURRENT \
52 | , parser->FOR##_mark \
53 | , p - parser->FOR##_mark \
54 | ); \
55 | }
56 | #define HEADER_CALLBACK(FOR) \
57 | if(CURRENT && parser->FOR##_mark && CURRENT->on_##FOR) { \
58 | CURRENT->on_##FOR( CURRENT \
59 | , parser->FOR##_mark \
60 | , p - parser->FOR##_mark \
61 | , CURRENT->number_of_headers \
62 | ); \
63 | }
64 | #define END_REQUEST \
65 | if(CURRENT && CURRENT->on_complete) \
66 | CURRENT->on_complete(CURRENT); \
67 | CURRENT = NULL;
68 |
69 |
70 | %%{
71 | machine ebb_request_parser;
72 |
73 | action mark_header_field { parser->header_field_mark = p; }
74 | action mark_header_value { parser->header_value_mark = p; }
75 | action mark_fragment { parser->fragment_mark = p; }
76 | action mark_query_string { parser->query_string_mark = p; }
77 | action mark_request_path { parser->path_mark = p; }
78 | action mark_request_uri { parser->uri_mark = p; }
79 |
80 | action write_field {
81 | HEADER_CALLBACK(header_field);
82 | parser->header_field_mark = NULL;
83 | }
84 |
85 | action write_value {
86 | HEADER_CALLBACK(header_value);
87 | parser->header_value_mark = NULL;
88 | }
89 |
90 | action request_uri {
91 | CALLBACK(uri);
92 | parser->uri_mark = NULL;
93 | }
94 |
95 | action fragment {
96 | CALLBACK(fragment);
97 | parser->fragment_mark = NULL;
98 | }
99 |
100 | action query_string {
101 | CALLBACK(query_string);
102 | parser->query_string_mark = NULL;
103 | }
104 |
105 | action request_path {
106 | CALLBACK(path);
107 | parser->path_mark = NULL;
108 | }
109 |
110 | action content_length {
111 | if(CURRENT){
112 | CURRENT->content_length *= 10;
113 | CURRENT->content_length += *p - '0';
114 | }
115 | }
116 |
117 | action use_identity_encoding { if(CURRENT) CURRENT->transfer_encoding = EBB_IDENTITY; }
118 | action use_chunked_encoding { if(CURRENT) CURRENT->transfer_encoding = EBB_CHUNKED; }
119 |
120 | action set_keep_alive { if(CURRENT) CURRENT->keep_alive = TRUE; }
121 | action set_not_keep_alive { if(CURRENT) CURRENT->keep_alive = FALSE; }
122 |
123 | action expect_continue {
124 | if(CURRENT) CURRENT->expect_continue = TRUE;
125 | }
126 |
127 | action trailer {
128 | /* not implemenetd yet. (do requests even have trailing headers?) */
129 | }
130 |
131 | action version_major {
132 | if(CURRENT) {
133 | CURRENT->version_major *= 10;
134 | CURRENT->version_major += *p - '0';
135 | }
136 | }
137 |
138 | action version_minor {
139 | if(CURRENT) {
140 | CURRENT->version_minor *= 10;
141 | CURRENT->version_minor += *p - '0';
142 | }
143 | }
144 |
145 | action end_header_line {
146 | if(CURRENT) CURRENT->number_of_headers++;
147 | }
148 |
149 | action end_headers {
150 | if(CURRENT && CURRENT->on_headers_complete)
151 | CURRENT->on_headers_complete(CURRENT);
152 | }
153 |
154 | action add_to_chunk_size {
155 | parser->chunk_size *= 16;
156 | parser->chunk_size += unhex[(int)*p];
157 | }
158 |
159 | action skip_chunk_data {
160 | skip_body(&p, parser, MIN(parser->chunk_size, REMAINING));
161 | fhold;
162 | if(parser->chunk_size > REMAINING) {
163 | fbreak;
164 | } else {
165 | fgoto chunk_end;
166 | }
167 | }
168 |
169 | action end_chunked_body {
170 | END_REQUEST;
171 | fnext main;
172 | }
173 |
174 | action start_req {
175 | assert(CURRENT == NULL);
176 | CURRENT = parser->new_request(parser->data);
177 | }
178 |
179 | action body_logic {
180 | if(CURRENT) {
181 | if(CURRENT->transfer_encoding == EBB_CHUNKED) {
182 | fnext ChunkedBody;
183 | } else {
184 | /* this is pretty stupid. i'd prefer to combine this with skip_chunk_data */
185 | parser->chunk_size = CURRENT->content_length;
186 | p += 1;
187 | skip_body(&p, parser, MIN(REMAINING, CURRENT->content_length));
188 | fhold;
189 | if(parser->chunk_size > REMAINING) {
190 | fbreak;
191 | }
192 | }
193 | }
194 | }
195 |
196 | #
197 | ##
198 | ###
199 | #### HTTP/1.1 STATE MACHINE
200 | ###
201 | ## RequestHeaders and character types are from
202 | # Zed Shaw's beautiful Mongrel parser.
203 |
204 | CRLF = "\r\n";
205 |
206 | # character types
207 | CTL = (cntrl | 127);
208 | safe = ("$" | "-" | "_" | ".");
209 | extra = ("!" | "*" | "'" | "(" | ")" | ",");
210 | reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+");
211 | unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">");
212 | national = any -- (alpha | digit | reserved | extra | safe | unsafe);
213 | unreserved = (alpha | digit | safe | extra | national);
214 | escape = ("%" xdigit xdigit);
215 | uchar = (unreserved | escape);
216 | pchar = (uchar | ":" | "@" | "&" | "=" | "+");
217 | tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
218 |
219 | # elements
220 | token = (ascii -- (CTL | tspecials));
221 | quote = "\"";
222 | # qdtext = token -- "\"";
223 | # quoted_pair = "\" ascii;
224 | # quoted_string = "\"" (qdtext | quoted_pair )* "\"";
225 |
226 | # headers
227 |
228 | Method = ( "COPY" %{ if(CURRENT) CURRENT->method = EBB_COPY; }
229 | | "DELETE" %{ if(CURRENT) CURRENT->method = EBB_DELETE; }
230 | | "GET" %{ if(CURRENT) CURRENT->method = EBB_GET; }
231 | | "HEAD" %{ if(CURRENT) CURRENT->method = EBB_HEAD; }
232 | | "LOCK" %{ if(CURRENT) CURRENT->method = EBB_LOCK; }
233 | | "MKCOL" %{ if(CURRENT) CURRENT->method = EBB_MKCOL; }
234 | | "MOVE" %{ if(CURRENT) CURRENT->method = EBB_MOVE; }
235 | | "OPTIONS" %{ if(CURRENT) CURRENT->method = EBB_OPTIONS; }
236 | | "POST" %{ if(CURRENT) CURRENT->method = EBB_POST; }
237 | | "PROPFIND" %{ if(CURRENT) CURRENT->method = EBB_PROPFIND; }
238 | | "PROPPATCH" %{ if(CURRENT) CURRENT->method = EBB_PROPPATCH; }
239 | | "PUT" %{ if(CURRENT) CURRENT->method = EBB_PUT; }
240 | | "TRACE" %{ if(CURRENT) CURRENT->method = EBB_TRACE; }
241 | | "UNLOCK" %{ if(CURRENT) CURRENT->method = EBB_UNLOCK; }
242 | ); # Not allowing extension methods
243 |
244 | HTTP_Version = "HTTP/" digit+ $version_major "." digit+ $version_minor;
245 |
246 | scheme = ( alpha | digit | "+" | "-" | "." )* ;
247 | absolute_uri = (scheme ":" (uchar | reserved )*);
248 | path = ( pchar+ ( "/" pchar* )* ) ;
249 | query = ( uchar | reserved )* >mark_query_string %query_string ;
250 | param = ( pchar | "/" )* ;
251 | params = ( param ( ";" param )* ) ;
252 | rel_path = ( path? (";" params)? ) ;
253 | absolute_path = ( "/"+ rel_path ) >mark_request_path %request_path ("?" query)?;
254 | Request_URI = ( "*" | absolute_uri | absolute_path ) >mark_request_uri %request_uri;
255 | Fragment = ( uchar | reserved )* >mark_fragment %fragment;
256 |
257 | field_name = ( token -- ":" )+;
258 | Field_Name = field_name >mark_header_field %write_field;
259 |
260 | field_value = ((any - " ") any*)?;
261 | Field_Value = field_value >mark_header_value %write_value;
262 |
263 | hsep = ":" " "*;
264 | header = (field_name hsep field_value) :> CRLF;
265 | Header = ( ("Content-Length"i hsep digit+ $content_length)
266 | | ("Connection"i hsep
267 | ( "Keep-Alive"i %set_keep_alive
268 | | "close"i %set_not_keep_alive
269 | )
270 | )
271 | | ("Transfer-Encoding"i %use_chunked_encoding hsep "identity" %use_identity_encoding)
272 | # | ("Expect"i hsep "100-continue"i %expect_continue)
273 | # | ("Trailer"i hsep field_value %trailer)
274 | | (Field_Name hsep Field_Value)
275 | ) :> CRLF;
276 |
277 | Request_Line = ( Method " " Request_URI ("#" Fragment)? " " HTTP_Version CRLF ) ;
278 | RequestHeader = Request_Line (Header %end_header_line)* :> CRLF @end_headers;
279 |
280 | # chunked message
281 | trailing_headers = header*;
282 | #chunk_ext_val = token | quoted_string;
283 | chunk_ext_val = token*;
284 | chunk_ext_name = token*;
285 | chunk_extension = ( ";" " "* chunk_ext_name ("=" chunk_ext_val)? )*;
286 | last_chunk = "0"+ chunk_extension CRLF;
287 | chunk_size = (xdigit* [1-9a-fA-F] xdigit*) $add_to_chunk_size;
288 | chunk_end = CRLF;
289 | chunk_body = any >skip_chunk_data;
290 | chunk_begin = chunk_size chunk_extension CRLF;
291 | chunk = chunk_begin chunk_body chunk_end;
292 | ChunkedBody := chunk* last_chunk trailing_headers CRLF @end_chunked_body;
293 |
294 | Request = RequestHeader >start_req @body_logic;
295 |
296 | main := Request*; # sequence of requests (for keep-alive)
297 | }%%
298 |
299 | %% write data;
300 |
301 | static void
302 | skip_body(const char **p, ebb_request_parser *parser, size_t nskip) {
303 | if(CURRENT && CURRENT->on_body && nskip > 0) {
304 | CURRENT->on_body(CURRENT, *p, nskip);
305 | }
306 | if(CURRENT) CURRENT->body_read += nskip;
307 | parser->chunk_size -= nskip;
308 | *p += nskip;
309 | if(0 == parser->chunk_size) {
310 | parser->eating = FALSE;
311 | if(CURRENT && CURRENT->transfer_encoding == EBB_IDENTITY) {
312 | END_REQUEST;
313 | }
314 | } else {
315 | parser->eating = TRUE;
316 | }
317 | }
318 |
319 | void ebb_request_parser_init(ebb_request_parser *parser)
320 | {
321 | int cs = 0;
322 | %% write init;
323 | parser->cs = cs;
324 |
325 | parser->chunk_size = 0;
326 | parser->eating = 0;
327 |
328 | parser->current_request = NULL;
329 |
330 | parser->header_field_mark = parser->header_value_mark =
331 | parser->query_string_mark = parser->path_mark =
332 | parser->uri_mark = parser->fragment_mark = NULL;
333 |
334 | parser->new_request = NULL;
335 | }
336 |
337 |
338 | /** exec **/
339 | size_t ebb_request_parser_execute(ebb_request_parser *parser, const char *buffer, size_t len)
340 | {
341 | const char *p, *pe;
342 | int cs = parser->cs;
343 |
344 | assert(parser->new_request && "undefined callback");
345 |
346 | p = buffer;
347 | pe = buffer+len;
348 |
349 | if(0 < parser->chunk_size && parser->eating) {
350 | /* eat body */
351 | size_t eat = MIN(len, parser->chunk_size);
352 | skip_body(&p, parser, eat);
353 | }
354 |
355 | if(parser->header_field_mark) parser->header_field_mark = buffer;
356 | if(parser->header_value_mark) parser->header_value_mark = buffer;
357 | if(parser->fragment_mark) parser->fragment_mark = buffer;
358 | if(parser->query_string_mark) parser->query_string_mark = buffer;
359 | if(parser->path_mark) parser->path_mark = buffer;
360 | if(parser->uri_mark) parser->uri_mark = buffer;
361 |
362 | %% write exec;
363 |
364 | parser->cs = cs;
365 |
366 | HEADER_CALLBACK(header_field);
367 | HEADER_CALLBACK(header_value);
368 | CALLBACK(fragment);
369 | CALLBACK(query_string);
370 | CALLBACK(path);
371 | CALLBACK(uri);
372 |
373 | assert(p <= pe && "buffer overflow after parsing execute");
374 |
375 | return(p - buffer);
376 | }
377 |
378 | int ebb_request_parser_has_error(ebb_request_parser *parser)
379 | {
380 | return parser->cs == ebb_request_parser_error;
381 | }
382 |
383 | int ebb_request_parser_is_finished(ebb_request_parser *parser)
384 | {
385 | return parser->cs == ebb_request_parser_first_final;
386 | }
387 |
388 | void ebb_request_init(ebb_request *request)
389 | {
390 | request->expect_continue = FALSE;
391 | request->body_read = 0;
392 | request->content_length = 0;
393 | request->version_major = 0;
394 | request->version_minor = 0;
395 | request->number_of_headers = 0;
396 | request->transfer_encoding = EBB_IDENTITY;
397 | request->keep_alive = -1;
398 |
399 | request->on_complete = NULL;
400 | request->on_headers_complete = NULL;
401 | request->on_body = NULL;
402 | request->on_header_field = NULL;
403 | request->on_header_value = NULL;
404 | request->on_uri = NULL;
405 | request->on_fragment = NULL;
406 | request->on_path = NULL;
407 | request->on_query_string = NULL;
408 | }
409 |
410 | int ebb_request_should_keep_alive(ebb_request *request)
411 | {
412 | if(request->keep_alive == -1)
413 | if(request->version_major == 1)
414 | return (request->version_minor != 0);
415 | else if(request->version_major == 0)
416 | return FALSE;
417 | else
418 | return TRUE;
419 | else
420 | return request->keep_alive;
421 | }
422 |
--------------------------------------------------------------------------------
/libebb/examples/ca-cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIBzDCCATegAwIBAgIESIuNVTALBgkqhkiG9w0BAQUwADAeFw0wODA3MjYyMDQ3
3 | MTlaFw0xMTA0MjIyMDQ3MjVaMAAwgZwwCwYJKoZIhvcNAQEBA4GMADCBiAKBgOm9
4 | l/FoXbTIcEusk/QlS5YrlR04+oWIbSdZIf3GJBEWEUPljDxAX96qHsTcaVnGK+EP
5 | keU4cIZvdY+hzbqa5cc1j2/9IeJNejL8gpQ/ocyMM69yq5Ib2F8K4mGWm1xr30hU
6 | bYpY5D0MrZ1b0HtYFVc8KVAr0ADGG+pye0P9c3B/AgMBAAGjWjBYMAwGA1UdEwEB
7 | /wQCMAAwFAYDVR0RBA0wC4IJbG9jYWxob3N0MBMGA1UdJQQMMAoGCCsGAQUFBwMB
8 | MB0GA1UdDgQWBBTYgVB7kJnnm+jgX9DgrapzGfUmxjALBgkqhkiG9w0BAQUDgYEA
9 | GkadA2H8CAzU3w4oCGZu9Ry9Tj/9Agw1XMFKvoJuG7VLPk7+B25JvNFVsmpROLxO
10 | 0TJ6mIU2hz5/rLvEfTBGQ+DYtbsjIxCz1fD7R5c1kKBtA0d0u8mY8pTlPNlxFPSW
11 | 3ymx5DB2zyDa/HuX6m6/VmzMYmA0vp7Dp1cl+pA9Nhs=
12 | -----END CERTIFICATE-----
13 |
--------------------------------------------------------------------------------
/libebb/examples/ca-key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICXQIBAAKBgQDpvZfxaF20yHBLrJP0JUuWK5UdOPqFiG0nWSH9xiQRFhFD5Yw8
3 | QF/eqh7E3GlZxivhD5HlOHCGb3WPoc26muXHNY9v/SHiTXoy/IKUP6HMjDOvcquS
4 | G9hfCuJhlptca99IVG2KWOQ9DK2dW9B7WBVXPClQK9AAxhvqcntD/XNwfwIDAQAB
5 | AoGAJqo3LTbfcV1KvinhG5zjwQaalwfq4RXtQHoNFmalZrIozvt01C6t7S5lApmX
6 | T8NpVMR3lNxeOM7NOqJAXuLqqVVqk81YEYuMx6E4gB/Ifl7jVZk1jstmLILhh59D
7 | pXrlpzvvm5X2hVsI7lp/YGAvtdLS1iVy37bGgmQWfCeeZiECQQDtZLfcJb4oE1yR
8 | ZfLOcPDlBCw02wGMNFpAjwbspf/du3Yn3ONWHVfhSCCcCe262h9PLblL97LoB+gF
9 | OHOlM9JvAkEA/A+U3/p9pwL4L742pxZP62/rmk6p5mZFIykk2jIUTpdilXBWBlcT
10 | OjgjnpquZpwnaClmvgFpkzdhIPF7Nq4K8QJBAITVKagOmnOUOeTF1fI78h9DkXTV
11 | 4uzP0nxzS52ZWS16Gqg9ihuCecz97flB+Prn2EMWw6tFY58/5U0ehF85OxMCQQDF
12 | 08TYdVSg+6emcPeb89sNwW18UjjuZ13j1qrhxWRCunXZK62YlEa27tCl7mjqh6w2
13 | CChm/9zIejJ1FJHLvJVBAkBj63ZbwggMYkxuj60jIBbNrEtDx9y7zM0sXkiJqcKp
14 | 5uGtJNafG+yZrLAHE6/b4aqUOtGsCGsiZpT9ms7CoaVr
15 | -----END RSA PRIVATE KEY-----
16 |
--------------------------------------------------------------------------------
/libebb/examples/hello_world.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #include
8 | #include "ebb.h"
9 |
10 | #define MSG ("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nhello world\n")
11 | static int c = 0;
12 |
13 | struct hello_connection {
14 | unsigned int responses_to_write;
15 | };
16 |
17 | void on_close(ebb_connection *connection)
18 | {
19 | free(connection->data);
20 | free(connection);
21 | }
22 |
23 | static void continue_responding(ebb_connection *connection)
24 | {
25 | int r;
26 | struct hello_connection *connection_data = connection->data;
27 | //printf("response complete \n");
28 | if(--connection_data->responses_to_write > 0) {
29 | /* write another response */
30 | r = ebb_connection_write(connection, MSG, sizeof MSG, continue_responding);
31 | assert(r);
32 | } else {
33 | ebb_connection_schedule_close(connection);
34 | }
35 | }
36 |
37 | static void request_complete(ebb_request *request)
38 | {
39 | //printf("request complete \n");
40 | ebb_connection *connection = request->data;
41 | struct hello_connection *connection_data = connection->data;
42 |
43 | if(ebb_request_should_keep_alive(request))
44 | connection_data->responses_to_write++;
45 | else
46 | connection_data->responses_to_write = 1;
47 |
48 | ebb_connection_write(connection, MSG, sizeof MSG, continue_responding);
49 | free(request);
50 | }
51 |
52 | static ebb_request* new_request(ebb_connection *connection)
53 | {
54 | //printf("request %d\n", ++c);
55 | ebb_request *request = malloc(sizeof(ebb_request));
56 | ebb_request_init(request);
57 | request->data = connection;
58 | request->on_complete = request_complete;
59 | return request;
60 | }
61 |
62 | ebb_connection* new_connection(ebb_server *server, struct sockaddr_in *addr)
63 | {
64 | struct hello_connection *connection_data = malloc(sizeof(struct hello_connection));
65 | if(connection_data == NULL)
66 | return NULL;
67 | connection_data->responses_to_write = 0;
68 |
69 | ebb_connection *connection = malloc(sizeof(ebb_connection));
70 | if(connection == NULL) {
71 | free(connection_data);
72 | return NULL;
73 | }
74 |
75 | ebb_connection_init(connection);
76 | connection->data = connection_data;
77 | connection->new_request = new_request;
78 | connection->on_close = on_close;
79 |
80 | printf("connection: %d\n", c++);
81 | return connection;
82 | }
83 |
84 | int main(int argc, char **_)
85 | {
86 | struct ev_loop *loop = ev_default_loop(0);
87 | ebb_server server;
88 |
89 | ebb_server_init(&server, loop);
90 | if(argc > 1) {
91 | printf("using SSL\n");
92 | ebb_server_set_secure(&server, "ca-cert.pem", "ca-key.pem");
93 | }
94 | server.new_connection = new_connection;
95 |
96 | printf("hello_world listening on port 5000\n");
97 | ebb_server_listen_on_port(&server, 5000);
98 | ev_loop(loop, 0);
99 |
100 | return 0;
101 | }
102 |
--------------------------------------------------------------------------------
/libebb/rbtree.c:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2008 Derrick Coetzee
2 | * http://en.literateprograms.org/Red-black_tree_(C)?oldid=7982
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a
5 | * copy of this software and associated documentation files (the
6 | * "Software"), to deal in the Software without restriction, including
7 | * without limitation the rights to use, copy, modify, merge, publish,
8 | * distribute, sublicense, and/or sell copies of the Software, and to permit
9 | * persons to whom the Software is furnished to do so, subject to the
10 | * following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included
13 | * in all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
20 | * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
21 | * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | */
23 |
24 | #include "rbtree.h"
25 | #include
26 |
27 | #ifndef NULL
28 | # define NULL ((void*)0)
29 | #endif
30 |
31 |
32 | typedef rbtree_node node;
33 | typedef enum rbtree_node_color color;
34 |
35 |
36 | static node grandparent(node n);
37 | static node sibling(node n);
38 | static node uncle(node n);
39 | static void verify_properties(rbtree t);
40 | /* static void verify_property_2(node root); */
41 | static color node_color(node n);
42 | /* static void verify_property_5(node root); */
43 |
44 | static node lookup_node(rbtree t, void* key);
45 | static void rotate_left(rbtree t, node n);
46 | static void rotate_right(rbtree t, node n);
47 |
48 | static void replace_node(rbtree t, node oldn, node newn);
49 | static void insert_case1(rbtree t, node n);
50 | static void insert_case2(rbtree t, node n);
51 | static void insert_case3(rbtree t, node n);
52 | static void insert_case4(rbtree t, node n);
53 | static void insert_case5(rbtree t, node n);
54 | static node maximum_node(node root);
55 | static void delete_case1(rbtree t, node n);
56 | static void delete_case2(rbtree t, node n);
57 | static void delete_case3(rbtree t, node n);
58 | static void delete_case4(rbtree t, node n);
59 | static void delete_case5(rbtree t, node n);
60 | static void delete_case6(rbtree t, node n);
61 |
62 | node grandparent(node n) {
63 | assert (n != NULL);
64 | assert (n->parent != NULL); /* Not the root node */
65 | assert (n->parent->parent != NULL); /* Not child of root */
66 | return n->parent->parent;
67 | }
68 |
69 | node sibling(node n) {
70 | assert (n != NULL);
71 | assert (n->parent != NULL); /* Root node has no sibling */
72 | if (n == n->parent->left)
73 | return n->parent->right;
74 | else
75 | return n->parent->left;
76 | }
77 |
78 | node uncle(node n) {
79 | assert (n != NULL);
80 | assert (n->parent != NULL); /* Root node has no uncle */
81 | assert (n->parent->parent != NULL); /* Children of root have no uncle */
82 | return sibling(n->parent);
83 | }
84 |
85 | void verify_properties(rbtree t) {
86 | #ifdef VERIFY_RBTREE
87 | verify_property_1(t->root);
88 | verify_property_2(t->root);
89 | /* Property 3 is implicit */
90 | verify_property_4(t->root);
91 | verify_property_5(t->root);
92 | #endif
93 | }
94 |
95 | /*
96 | void verify_property_2(node root) {
97 | assert(node_color(root) == BLACK);
98 | }
99 | */
100 |
101 | color node_color(node n) {
102 | return n == NULL ? BLACK : n->color;
103 | }
104 |
105 | /*
106 | void verify_property_5(node root) {
107 | int black_count_path = -1;
108 | verify_property_5_helper(root, 0, &black_count_path);
109 | }
110 | */
111 |
112 | void rbtree_init(rbtree t, rbtree_compare_func compare) {
113 | t->root = NULL;
114 | t->compare = compare;
115 | verify_properties(t);
116 | }
117 |
118 | node lookup_node(rbtree t, void* key) {
119 | node n = t->root;
120 | while (n != NULL) {
121 | int comp_result = t->compare(key, n->key);
122 | if (comp_result == 0) {
123 | return n;
124 | } else if (comp_result < 0) {
125 | n = n->left;
126 | } else {
127 | assert(comp_result > 0);
128 | n = n->right;
129 | }
130 | }
131 | return n;
132 | }
133 |
134 | void* rbtree_lookup(rbtree t, void* key) {
135 | node n = lookup_node(t, key);
136 | return n == NULL ? NULL : n->value;
137 | }
138 |
139 | void rotate_left(rbtree t, node n) {
140 | node r = n->right;
141 | replace_node(t, n, r);
142 | n->right = r->left;
143 | if (r->left != NULL) {
144 | r->left->parent = n;
145 | }
146 | r->left = n;
147 | n->parent = r;
148 | }
149 |
150 | void rotate_right(rbtree t, node n) {
151 | node L = n->left;
152 | replace_node(t, n, L);
153 | n->left = L->right;
154 | if (L->right != NULL) {
155 | L->right->parent = n;
156 | }
157 | L->right = n;
158 | n->parent = L;
159 | }
160 |
161 | void replace_node(rbtree t, node oldn, node newn) {
162 | if (oldn->parent == NULL) {
163 | t->root = newn;
164 | } else {
165 | if (oldn == oldn->parent->left)
166 | oldn->parent->left = newn;
167 | else
168 | oldn->parent->right = newn;
169 | }
170 | if (newn != NULL) {
171 | newn->parent = oldn->parent;
172 | }
173 | }
174 |
175 | void rbtree_insert(rbtree t, rbtree_node inserted_node) {
176 | inserted_node->color = RED;
177 | inserted_node->left = NULL;
178 | inserted_node->right = NULL;
179 | inserted_node->parent = NULL;
180 |
181 | if (t->root == NULL) {
182 | t->root = inserted_node;
183 | } else {
184 | node n = t->root;
185 | while (1) {
186 | int comp_result = t->compare(inserted_node->key, n->key);
187 | if (comp_result == 0) {
188 | n->value = inserted_node->value;
189 | return;
190 | } else if (comp_result < 0) {
191 | if (n->left == NULL) {
192 | n->left = inserted_node;
193 | break;
194 | } else {
195 | n = n->left;
196 | }
197 | } else {
198 | assert (comp_result > 0);
199 | if (n->right == NULL) {
200 | n->right = inserted_node;
201 | break;
202 | } else {
203 | n = n->right;
204 | }
205 | }
206 | }
207 | inserted_node->parent = n;
208 | }
209 | insert_case1(t, inserted_node);
210 | verify_properties(t);
211 | }
212 |
213 | void insert_case1(rbtree t, node n) {
214 | if (n->parent == NULL)
215 | n->color = BLACK;
216 | else
217 | insert_case2(t, n);
218 | }
219 |
220 | void insert_case2(rbtree t, node n) {
221 | if (node_color(n->parent) == BLACK)
222 | return; /* Tree is still valid */
223 | else
224 | insert_case3(t, n);
225 | }
226 |
227 | void insert_case3(rbtree t, node n) {
228 | if (node_color(uncle(n)) == RED) {
229 | n->parent->color = BLACK;
230 | uncle(n)->color = BLACK;
231 | grandparent(n)->color = RED;
232 | insert_case1(t, grandparent(n));
233 | } else {
234 | insert_case4(t, n);
235 | }
236 | }
237 |
238 | void insert_case4(rbtree t, node n) {
239 | if (n == n->parent->right && n->parent == grandparent(n)->left) {
240 | rotate_left(t, n->parent);
241 | n = n->left;
242 | } else if (n == n->parent->left && n->parent == grandparent(n)->right) {
243 | rotate_right(t, n->parent);
244 | n = n->right;
245 | }
246 | insert_case5(t, n);
247 | }
248 |
249 | void insert_case5(rbtree t, node n) {
250 | n->parent->color = BLACK;
251 | grandparent(n)->color = RED;
252 | if (n == n->parent->left && n->parent == grandparent(n)->left) {
253 | rotate_right(t, grandparent(n));
254 | } else {
255 | assert (n == n->parent->right && n->parent == grandparent(n)->right);
256 | rotate_left(t, grandparent(n));
257 | }
258 | }
259 |
260 | rbtree_node rbtree_delete(rbtree t, void* key) {
261 | node child;
262 | node n = lookup_node(t, key);
263 | if (n == NULL) return NULL; /* Key not found, do nothing */
264 | if (n->left != NULL && n->right != NULL) {
265 | /* Copy key/value from predecessor and then delete it instead */
266 | node pred = maximum_node(n->left);
267 | n->key = pred->key;
268 | n->value = pred->value;
269 | n = pred;
270 | }
271 |
272 | assert(n->left == NULL || n->right == NULL);
273 | child = n->right == NULL ? n->left : n->right;
274 | if (node_color(n) == BLACK) {
275 | n->color = node_color(child);
276 | delete_case1(t, n);
277 | }
278 | replace_node(t, n, child);
279 |
280 | verify_properties(t);
281 | return n;
282 | }
283 |
284 | static node maximum_node(node n) {
285 | assert (n != NULL);
286 | while (n->right != NULL) {
287 | n = n->right;
288 | }
289 | return n;
290 | }
291 |
292 | void delete_case1(rbtree t, node n) {
293 | if (n->parent == NULL)
294 | return;
295 | else
296 | delete_case2(t, n);
297 | }
298 |
299 | void delete_case2(rbtree t, node n) {
300 | if (node_color(sibling(n)) == RED) {
301 | n->parent->color = RED;
302 | sibling(n)->color = BLACK;
303 | if (n == n->parent->left)
304 | rotate_left(t, n->parent);
305 | else
306 | rotate_right(t, n->parent);
307 | }
308 | delete_case3(t, n);
309 | }
310 |
311 | void delete_case3(rbtree t, node n) {
312 | if (node_color(n->parent) == BLACK &&
313 | node_color(sibling(n)) == BLACK &&
314 | node_color(sibling(n)->left) == BLACK &&
315 | node_color(sibling(n)->right) == BLACK)
316 | {
317 | sibling(n)->color = RED;
318 | delete_case1(t, n->parent);
319 | }
320 | else
321 | delete_case4(t, n);
322 | }
323 |
324 | void delete_case4(rbtree t, node n) {
325 | if (node_color(n->parent) == RED &&
326 | node_color(sibling(n)) == BLACK &&
327 | node_color(sibling(n)->left) == BLACK &&
328 | node_color(sibling(n)->right) == BLACK)
329 | {
330 | sibling(n)->color = RED;
331 | n->parent->color = BLACK;
332 | }
333 | else
334 | delete_case5(t, n);
335 | }
336 |
337 | void delete_case5(rbtree t, node n) {
338 | if (n == n->parent->left &&
339 | node_color(sibling(n)) == BLACK &&
340 | node_color(sibling(n)->left) == RED &&
341 | node_color(sibling(n)->right) == BLACK)
342 | {
343 | sibling(n)->color = RED;
344 | sibling(n)->left->color = BLACK;
345 | rotate_right(t, sibling(n));
346 | }
347 | else if (n == n->parent->right &&
348 | node_color(sibling(n)) == BLACK &&
349 | node_color(sibling(n)->right) == RED &&
350 | node_color(sibling(n)->left) == BLACK)
351 | {
352 | sibling(n)->color = RED;
353 | sibling(n)->right->color = BLACK;
354 | rotate_left(t, sibling(n));
355 | }
356 | delete_case6(t, n);
357 | }
358 |
359 | void delete_case6(rbtree t, node n) {
360 | sibling(n)->color = node_color(n->parent);
361 | n->parent->color = BLACK;
362 | if (n == n->parent->left) {
363 | assert (node_color(sibling(n)->right) == RED);
364 | sibling(n)->right->color = BLACK;
365 | rotate_left(t, n->parent);
366 | }
367 | else
368 | {
369 | assert (node_color(sibling(n)->left) == RED);
370 | sibling(n)->left->color = BLACK;
371 | rotate_right(t, n->parent);
372 | }
373 | }
374 |
375 |
376 |
--------------------------------------------------------------------------------
/libebb/rbtree.h:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2008 Derrick Coetzee
2 | * http://en.literateprograms.org/Red-black_tree_(C)?oldid=7982
3 | * Small changes by Ryah Dahl
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a
6 | * copy of this software and associated documentation files (the
7 | * "Software"), to deal in the Software without restriction, including
8 | * without limitation the rights to use, copy, modify, merge, publish,
9 | * distribute, sublicense, and/or sell copies of the Software, and to permit
10 | * persons to whom the Software is furnished to do so, subject to the
11 | * following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included
14 | * in all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
21 | * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
22 | * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 | */
24 |
25 | #ifndef _RBTREE_H_
26 | #define _RBTREE_H_
27 |
28 | enum rbtree_node_color { RED, BLACK };
29 |
30 | typedef int (*rbtree_compare_func)(void* left_key, void* right_key);
31 |
32 | typedef struct rbtree_node_t {
33 | struct rbtree_node_t* left; /* private */
34 | struct rbtree_node_t* right; /* private */
35 | struct rbtree_node_t* parent; /* private */
36 | enum rbtree_node_color color; /* private */
37 | void* key; /* public */
38 | void* value; /* public */
39 | } *rbtree_node;
40 |
41 | typedef struct rbtree_t {
42 | rbtree_node root; /* private */
43 | rbtree_compare_func compare; /* private */
44 | } *rbtree;
45 |
46 |
47 | void rbtree_init(rbtree t, rbtree_compare_func);
48 | void* rbtree_lookup(rbtree t, void* key);
49 | void rbtree_insert(rbtree t, rbtree_node);
50 | /* you must free the returned node */
51 | rbtree_node rbtree_delete(rbtree t, void* key);
52 |
53 | #endif
54 |
55 |
--------------------------------------------------------------------------------
/libebb/test_examples.rb:
--------------------------------------------------------------------------------
1 | require 'test/unit'
2 | require 'socket'
3 |
4 | REQ = "GET /hello/%d HTTP/1.1\r\n\r\n"
5 | HOST = '0.0.0.0'
6 | PORT = 5000
7 |
8 | class TCPSocket
9 | def full_send(string)
10 | written = 0
11 | while(written < string.length)
12 | sent = write(string)
13 | string = string.slice(sent, 10000)
14 | end
15 | written
16 | end
17 |
18 | def full_read
19 | response = ""
20 | while chunk = read(10000)
21 | response += chunk
22 | end
23 | response
24 | end
25 |
26 | end
27 |
28 | class EbbTest < Test::Unit::TestCase
29 |
30 | def setup
31 | @socket = TCPSocket.new(HOST, PORT)
32 | end
33 |
34 | def test_bad_req
35 | @socket.full_send("hello")
36 | assert_equal "", @socket.full_read
37 | #assert @socket.closed?
38 | end
39 |
40 | def test_single
41 | written = 0
42 | req = REQ % 1
43 | @socket.full_send(req)
44 | response = @socket.full_read()
45 | count = 0
46 | response.scan("hello world") { count += 1 }
47 |
48 | assert_equal 1, count
49 | end
50 |
51 | def test_pipeline
52 | written = 0
53 | req = (REQ % 1) + (REQ % 2) + (REQ % 3) + (REQ % 4)
54 | @socket.full_send(req)
55 | response = @socket.full_read()
56 | count = 0
57 | response.scan("hello world") { count += 1 }
58 | assert_equal 4, count
59 | end
60 | end
61 |
--------------------------------------------------------------------------------
/libebb/test_rbtree.c:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2008 Derrick Coetzee
2 | * http://en.literateprograms.org/Red-black_tree_(C)?oldid=7982
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a
5 | * copy of this software and associated documentation files (the
6 | * "Software"), to deal in the Software without restriction, including
7 | * without limitation the rights to use, copy, modify, merge, publish,
8 | * distribute, sublicense, and/or sell copies of the Software, and to permit
9 | * persons to whom the Software is furnished to do so, subject to the
10 | * following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included
13 | * in all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
20 | * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
21 | * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | */
23 |
24 | #include "rbtree.h"
25 | #include
26 | #include
27 | #include /* rand(), malloc(), free() */
28 |
29 | static int compare_int(void* left, void* right);
30 | static void print_tree(rbtree t);
31 | static void print_tree_helper(rbtree_node n, int indent);
32 |
33 | int compare_int(void* leftp, void* rightp) {
34 | int left = (int)leftp;
35 | int right = (int)rightp;
36 | if (left < right)
37 | return -1;
38 | else if (left > right)
39 | return 1;
40 | else {
41 | assert (left == right);
42 | return 0;
43 | }
44 | }
45 |
46 | #define INDENT_STEP 4
47 |
48 | void print_tree_helper(rbtree_node n, int indent);
49 |
50 | void print_tree(rbtree t) {
51 | print_tree_helper(t->root, 0);
52 | puts("");
53 | }
54 |
55 | void print_tree_helper(rbtree_node n, int indent) {
56 | int i;
57 | if (n == NULL) {
58 | fputs("", stdout);
59 | return;
60 | }
61 | if (n->right != NULL) {
62 | print_tree_helper(n->right, indent + INDENT_STEP);
63 | }
64 | for(i=0; icolor == BLACK)
67 | printf("%d\n", (int)n->key);
68 | else
69 | printf("<%d>\n", (int)n->key);
70 | if (n->left != NULL) {
71 | print_tree_helper(n->left, indent + INDENT_STEP);
72 | }
73 | }
74 |
75 | int main() {
76 | int i;
77 | struct rbtree_t t;
78 | rbtree_init(&t, compare_int);
79 | print_tree(&t);
80 |
81 | for(i=0; i<5000; i++) {
82 | int x = rand() % 10000;
83 | int y = rand() % 10000;
84 | #ifdef TRACE
85 | print_tree(&t);
86 | printf("Inserting %d -> %d\n\n", x, y);
87 | #endif
88 | rbtree_node node = (rbtree_node) malloc(sizeof(struct rbtree_node_t));
89 | node->key = (void*)x;
90 | node->value = (void*)y;
91 | rbtree_insert(&t, node);
92 | assert(rbtree_lookup(&t, (void*)x) == (void*)y);
93 | }
94 | for(i=0; i<60000; i++) {
95 | int x = rand() % 10000;
96 | #ifdef TRACE
97 | print_tree(&t);
98 | printf("Deleting key %d\n\n", x);
99 | #endif
100 | rbtree_node n = rbtree_delete(&t, (void*)x);
101 | if(n != NULL) {
102 | free(n);
103 | }
104 | }
105 | printf("Okay\n");
106 | return 0;
107 | }
108 |
109 |
--------------------------------------------------------------------------------
/midconnlist.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include
19 | #include "midconnlist.h"
20 |
21 | struct midconn_node
22 | {
23 | RB_ENTRY(midconn_node) entry;
24 | int mid;
25 | uint32_t connid;
26 | };
27 |
28 | static int midconn_cmp(struct midconn_node *e1, struct midconn_node *e2)
29 | {
30 | return (e1->mid < e2->mid ? -1 : e1->mid > e2->mid);
31 | }
32 |
33 | RB_HEAD(midconnlist, midconn_node) midconn_head = RB_INITIALIZER(&midconn_head);
34 | RB_PROTOTYPE(midconnlist, midconn_node, entry, midconn_cmp);
35 | RB_GENERATE(midconnlist, midconn_node, entry, midconn_cmp);
36 |
37 | int midconn_find(int mid, uint32_t *connid)
38 | {
39 | struct midconn_node key;
40 | struct midconn_node *res;
41 | key.mid = mid;
42 | res = RB_FIND(midconnlist, &midconn_head, &key);
43 | if (NULL == res)
44 | return 1;
45 | *connid = res->connid;
46 | return 0;
47 | }
48 |
49 | int midconn_insert(int mid, uint32_t connid)
50 | {
51 | struct midconn_node *data;
52 |
53 | if (NULL == (data = malloc(sizeof(struct midconn_node))))
54 | return 1;
55 |
56 | data->mid = mid;
57 | data->connid = connid;
58 |
59 | if (NULL == RB_INSERT(midconnlist, &midconn_head, data)) // NULL means OK
60 | {
61 | return 0;
62 | }
63 | else
64 | {
65 | LOG_CRITICAL("middconn_insert mid collision"); // can't happen
66 | return 1;
67 | }
68 | }
69 |
70 | int midconn_remove(int mid)
71 | {
72 | struct midconn_node key;
73 | struct midconn_node *res;
74 | key.mid = mid;
75 | res = RB_FIND(midconnlist, &midconn_head, &key);
76 | if (NULL == res)
77 | return 1;
78 | RB_REMOVE(midconnlist, &midconn_head, res);
79 | free(res);
80 | return 0;
81 | }
82 |
83 | void midconn_print(void)
84 | {
85 | struct midconn_node *i;
86 | RB_FOREACH(i, midconnlist, &midconn_head)
87 | {
88 | LOG_INFO("mid=%d -> connid=%d\n", i->mid, i->connid);
89 | }
90 | }
91 |
92 | void midconn_foreach(midconn_iterate_func f, void *userdata)
93 | {
94 | struct midconn_node *i;
95 | RB_FOREACH(i, midconnlist, &midconn_head)
96 | {
97 | f(i->mid, i->connid, userdata);
98 | }
99 | }
100 |
101 | #if 0
102 | void midconn_test(void)
103 | {
104 | uint32_t connid;
105 |
106 | midconn_insert(10, 100);
107 | midconn_insert(20, 200);
108 | midconn_insert(30, 300);
109 |
110 | midconn_print();
111 |
112 | if (midconn_find(20, &connid) < 0)
113 | LOG_CRITICAL("Sanity failure");
114 | LOG_INFO("mid 20 found on -> connid %d", connid);
115 | }
116 | #endif
117 |
118 |
--------------------------------------------------------------------------------
/midconnlist.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef MIDCONNLIST_H
17 | #define MIDCONNLIST_H 1
18 |
19 | typedef void (*midconn_iterate_func)(int mid, uint32_t connid, void *userdata);
20 |
21 | extern int midconn_find(int mid, uint32_t *connid);
22 | extern int midconn_insert(int mid, uint32_t connid);
23 | extern int midconn_remove(int mid);
24 | extern void midconn_foreach(midconn_iterate_func f, void *userdata);
25 | extern void midconn_print(void);
26 |
27 | #endif
28 |
29 |
--------------------------------------------------------------------------------
/mqtt.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #include "mqtthttpd.h"
22 | #include "evhelper.h"
23 | #include "mqtt.h"
24 | #include "webapi.h"
25 |
26 | extern server_context_t g_srvctx;
27 |
28 | #if LIBMOSQUITTO_VERSION_NUMBER <= 1000000
29 | #define MOSQ_MID_T uint16_t
30 | #else
31 | #define MOSQ_MID_T int
32 | #endif
33 |
34 | static void mqtt_timeout_cb(struct ev_loop *loop, struct ev_io *w, int revents)
35 | {
36 | ev_timer_helper_t *watcher = (ev_timer_helper_t *)w;
37 | mqtt_context_t *mqttctx = (mqtt_context_t *)watcher->userdata;
38 |
39 | if (EV_TIMEOUT & revents)
40 | {
41 | if (mqttctx->connected)
42 | {
43 | if (MOSQ_ERR_SUCCESS != mosquitto_loop_misc(mqttctx->mosq))
44 | {
45 | LOG_ERROR("mosquitto_loop_misc fail");
46 | mqtt_disconnect(mqttctx);
47 | }
48 |
49 | #if LIBMOSQUITTO_VERSION_NUMBER > 1000000
50 | if (mosquitto_want_write(mqttctx->mosq))
51 | #endif
52 | ev_io_start(g_srvctx.loop, &mqttctx->write_watcher.io);
53 | }
54 | }
55 | }
56 |
57 | static void mqtt_ev_cb(struct ev_loop *loop, ev_io *w, int revents)
58 | {
59 | ev_io_helper_t *watcher = (ev_io_helper_t *)w;
60 | mqtt_context_t *mqttctx = (mqtt_context_t *)watcher->userdata;
61 |
62 | if (EV_READ & revents)
63 | {
64 | if (MOSQ_ERR_SUCCESS != mosquitto_loop_read(mqttctx->mosq
65 | #if LIBMOSQUITTO_VERSION_NUMBER > 1000000
66 | , 1
67 | #endif
68 | ))
69 | {
70 | LOG_ERROR("read fail");
71 | mqtt_disconnect(mqttctx);
72 | }
73 | }
74 |
75 | if (EV_WRITE & revents)
76 | {
77 | if (MOSQ_ERR_SUCCESS != mosquitto_loop_write(mqttctx->mosq
78 | #if LIBMOSQUITTO_VERSION_NUMBER > 1000000
79 | , 1
80 | #endif
81 | ))
82 | {
83 | LOG_ERROR("write fail");
84 | mqtt_disconnect(mqttctx);
85 | }
86 |
87 | #if LIBMOSQUITTO_VERSION_NUMBER > 1000000
88 | if (!mosquitto_want_write(mqttctx->mosq))
89 | #endif
90 | ev_io_stop(g_srvctx.loop, &mqttctx->write_watcher.io);
91 | }
92 | }
93 |
94 | static void mosq_connect_cb(
95 | #if LIBMOSQUITTO_VERSION_NUMBER > 1000000
96 | struct mosquitto *mosq,
97 | #endif
98 | void *userdata, int rc)
99 | {
100 | mqtt_context_t *mqttctx = (mqtt_context_t *)userdata;
101 |
102 | if (rc != 0)
103 | LOG_ERROR("mqtt connect failure %d", rc);
104 | else
105 | {
106 | LOG_INFO("MQTT connected");
107 | mqttctx->connected = true;
108 | }
109 | }
110 |
111 | static void mosq_message_cb(
112 | #if LIBMOSQUITTO_VERSION_NUMBER > 1000000
113 | struct mosquitto *mosq,
114 | #endif
115 | void *userdata, const struct mosquitto_message *msg)
116 | {
117 | if (msg->payloadlen)
118 | {
119 | LOG_INFO("rx %s %s", msg->topic, msg->payload);
120 | webapi_message_cb(msg->mid, msg->topic, (char *)msg->payload);
121 | }
122 | else
123 | {
124 | LOG_INFO("%s (null)", msg->topic);
125 | webapi_message_cb(msg->mid, msg->topic, "");
126 | }
127 | }
128 |
129 | static void mosq_unsubscribe_cb(
130 | #if LIBMOSQUITTO_VERSION_NUMBER > 1000000
131 | struct mosquitto *mosq,
132 | #endif
133 | void *userdata, MOSQ_MID_T mid)
134 | {
135 | LOG_INFO("unsub OK mid=%d", mid);
136 | }
137 |
138 | static void mosq_subscribe_cb(
139 | #if LIBMOSQUITTO_VERSION_NUMBER > 1000000
140 | struct mosquitto *mosq,
141 | void *userdata, MOSQ_MID_T mid, int qos_count, const int *granted_qos)
142 | #else
143 | void *userdata, MOSQ_MID_T mid, int qos_count, const uint8_t *granted_qos)
144 | #endif
145 | {
146 | LOG_INFO("sub OK mid=%d", mid);
147 | webapi_subscribe_cb(mid);
148 | }
149 |
150 | static void mosq_publish_cb(
151 | #if LIBMOSQUITTO_VERSION_NUMBER > 1000000
152 | struct mosquitto *mosq,
153 | #endif
154 | void *userdata, MOSQ_MID_T mid)
155 | {
156 | LOG_INFO("pub OK mid=%d", mid);
157 | webapi_publish_cb(mid);
158 | }
159 |
160 |
161 | int mqtt_init(struct ev_loop *loop, mqtt_context_t *mqttctx)
162 | {
163 | if (MOSQ_ERR_SUCCESS != mosquitto_lib_init())
164 | return -1;
165 |
166 | mqttctx->connected = false;
167 | return 0;
168 | }
169 |
170 | int mqtt_connect(mqtt_context_t *mqttctx, const char *name, const char *host, uint16_t port) // FIXME, blocks
171 | {
172 | ev_io *w;
173 | ev_timer *t;
174 |
175 | if (NULL != mqttctx->mosq)
176 | mosquitto_destroy(mqttctx->mosq);
177 |
178 | #if LIBMOSQUITTO_VERSION_NUMBER > 1000000
179 | if (NULL == (mqttctx->mosq = mosquitto_new(name, true, (void *)mqttctx)))
180 | #else
181 | if (NULL == (mqttctx->mosq = mosquitto_new(name, (void *)mqttctx)))
182 | #endif
183 | {
184 | LOG_ERROR("mqtt context creation failed");
185 | return 1;
186 | }
187 |
188 | mosquitto_connect_callback_set(mqttctx->mosq, mosq_connect_cb);
189 | mosquitto_message_callback_set(mqttctx->mosq, mosq_message_cb);
190 | mosquitto_subscribe_callback_set(mqttctx->mosq, mosq_subscribe_cb);
191 | mosquitto_unsubscribe_callback_set(mqttctx->mosq, mosq_unsubscribe_cb);
192 | mosquitto_publish_callback_set(mqttctx->mosq, mosq_publish_cb);
193 |
194 | if (0 != strcmp(host, g_srvctx.mqtt_name))
195 | strncpy(g_srvctx.mqtt_name, name, sizeof(g_srvctx.mqtt_name));
196 | if (0 != strcmp(host, g_srvctx.mqtt_server))
197 | strncpy(g_srvctx.mqtt_server, host, sizeof(g_srvctx.mqtt_server));
198 | g_srvctx.mqtt_port = port;
199 |
200 | #if LIBMOSQUITTO_VERSION_NUMBER <= 1000000
201 | if (0 != mosquitto_connect(mqttctx->mosq, g_srvctx.mqtt_server, g_srvctx.mqtt_port, 10, true)) // FIXME, this should be async, with below in cb
202 | #else
203 | if (0 != mosquitto_connect(mqttctx->mosq, g_srvctx.mqtt_server, g_srvctx.mqtt_port, 10)) // FIXME, this should be async, with below in cb
204 | #endif
205 | {
206 | LOG_INFO("mqtt connect failed");
207 | return 1;
208 | }
209 |
210 | w = &mqttctx->read_watcher.io;
211 | mqttctx->read_watcher.userdata = (void *)mqttctx;
212 | ev_io_init(w, mqtt_ev_cb, mosquitto_socket(mqttctx->mosq), EV_READ);
213 | ev_io_start(g_srvctx.loop, w);
214 |
215 | w = &mqttctx->write_watcher.io;
216 | mqttctx->write_watcher.userdata = (void *)mqttctx;
217 | ev_io_init(w, mqtt_ev_cb, mosquitto_socket(mqttctx->mosq), EV_WRITE);
218 |
219 | t = &mqttctx->timer.timer;
220 | mqttctx->timer.userdata = (void *)mqttctx;
221 | ev_timer_init(t, (void *)mqtt_timeout_cb, 0, 0.1);
222 | ev_timer_start(g_srvctx.loop, t);
223 |
224 | return 0;
225 | }
226 |
227 | int mqtt_disconnect(mqtt_context_t *mqttctx)
228 | {
229 | LOG_INFO("mqtt_disconnect");
230 |
231 | mosquitto_disconnect(mqttctx->mosq);
232 | ev_io_stop(g_srvctx.loop, &mqttctx->write_watcher.io);
233 | ev_io_stop(g_srvctx.loop, &mqttctx->read_watcher.io);
234 | ev_timer_stop(g_srvctx.loop, &mqttctx->timer.timer);
235 | mqttctx->connected = false;
236 |
237 | webapi_mqtt_failall_cb(); // fail any connections waiting on a pub response
238 |
239 | LOG_CRITICAL("mqtt connection broken"); // FIXME, we should restart
240 |
241 | return 0;
242 | }
243 |
244 |
245 | int mqtt_publish(mqtt_context_t *mqttctx, const char *topic, const char *msg, int qos, int *mid)
246 | {
247 | int rc;
248 | MOSQ_MID_T m;
249 |
250 | rc = mosquitto_publish(mqttctx->mosq, &m, topic, strlen(msg), (const uint8_t *)msg, qos, true);
251 | LOG_DEBUG("mqtt_publish topic=%s msg=%s rc=%d", topic, msg, rc);
252 |
253 | if (NULL != mid)
254 | *mid = m;
255 |
256 | #if LIBMOSQUITTO_VERSION_NUMBER > 1000000
257 | if (mosquitto_want_write(mqttctx->mosq))
258 | #endif
259 | ev_io_start(g_srvctx.loop, &mqttctx->write_watcher.io);
260 |
261 | return rc;
262 | }
263 |
264 | int mqtt_subscribe(mqtt_context_t *mqttctx, const char *topic, int qos, int *mid)
265 | {
266 | int rc;
267 | MOSQ_MID_T m;
268 |
269 | rc = mosquitto_subscribe(mqttctx->mosq, &m, topic, qos);
270 | LOG_DEBUG("mqtt_subscribe topic=%s rc=%d", topic, rc);
271 |
272 | if (NULL != mid)
273 | *mid = m;
274 |
275 | #if LIBMOSQUITTO_VERSION_NUMBER > 1000000
276 | if (mosquitto_want_write(mqttctx->mosq))
277 | #endif
278 | ev_io_start(g_srvctx.loop, &mqttctx->write_watcher.io);
279 |
280 | return rc;
281 | }
282 |
283 | int mqtt_unsubscribe(mqtt_context_t *mqttctx, const char *topic, int *mid)
284 | {
285 | int rc;
286 | MOSQ_MID_T m;
287 |
288 | rc = mosquitto_unsubscribe(mqttctx->mosq, &m, topic);
289 | LOG_DEBUG("mqtt_unsubscribe topic=%s rc=%d", topic, rc);
290 |
291 | if (NULL != mid)
292 | *mid = m;
293 |
294 | #if LIBMOSQUITTO_VERSION_NUMBER > 1000000
295 | if (mosquitto_want_write(mqttctx->mosq))
296 | #endif
297 | ev_io_start(g_srvctx.loop, &mqttctx->write_watcher.io);
298 |
299 | return rc;
300 | }
301 |
302 |
--------------------------------------------------------------------------------
/mqtt.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef MQTT_H
17 | #define MQTT_H 1
18 |
19 | #include "evhelper.h"
20 |
21 | struct mqtt_context
22 | {
23 | struct mosquitto *mosq;
24 | ev_io_helper_t read_watcher;
25 | ev_io_helper_t write_watcher;
26 | ev_timer_helper_t timer;
27 | bool connected;
28 | };
29 | typedef struct mqtt_context mqtt_context_t;
30 |
31 | extern int mqtt_init(struct ev_loop *loop, mqtt_context_t *mqctx);
32 | extern int mqtt_connect(mqtt_context_t *mqctx, const char *name, const char *host, uint16_t port);
33 | extern int mqtt_disconnect(mqtt_context_t *mqctx);
34 | extern int mqtt_publish(mqtt_context_t *mqctx, const char *topic, const char *msg, int qos, int *mid);
35 | extern int mqtt_subscribe(mqtt_context_t *mqttctx, const char *topic, int qos, int *mid);
36 | extern int mqtt_unsubscribe(mqtt_context_t *mqttctx, const char *topic, int *mid);
37 |
38 | #endif
39 |
40 |
--------------------------------------------------------------------------------
/mqtthttpd.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 |
28 | #include "mqtthttpd.h"
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 |
35 | #include "mqtt.h"
36 | #include "httpd.h"
37 | #include "stream.h"
38 | #include "streamlist.h"
39 | #include "accesslog.h"
40 | #include "opt.h"
41 |
42 | server_context_t g_srvctx;
43 |
44 | void exitfunc(void)
45 | {
46 | }
47 |
48 | void idle_cb(struct ev_loop *loop, struct ev_idle *w, int revents)
49 | {
50 | }
51 |
52 | void timeout_1hz_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
53 | {
54 | if (EV_TIMEOUT & revents)
55 | {
56 | streamlist_foreach(stream_1hz, NULL);
57 | }
58 | }
59 |
60 | void timeout_10hz_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
61 | {
62 | if (EV_TIMEOUT & revents)
63 | {
64 | }
65 | }
66 |
67 |
68 | int main(int argc, char *argv[])
69 | {
70 | if (0 == getuid())
71 | LOG_CRITICAL("Don't run as root!");
72 |
73 | memset(&g_srvctx, 0, sizeof(g_srvctx));
74 |
75 | signal(SIGPIPE, SIG_IGN); // ignore sigpipe
76 | g_srvctx.loop = ev_default_loop(0);
77 |
78 | log_init();
79 | random_init();
80 |
81 | if (0 != accesslog_init())
82 | LOG_CRITICAL("Failed to open access log");
83 | if (NULL == (g_srvctx.timer_1hz = (struct ev_timer *)malloc(sizeof(struct ev_timer))))
84 | LOG_CRITICAL("out of mem");
85 | if (NULL == (g_srvctx.timer_10hz = (struct ev_timer *)malloc(sizeof(struct ev_timer))))
86 | LOG_CRITICAL("out of mem");
87 | if (NULL == (g_srvctx.idle_watcher = (struct ev_idle *)malloc(sizeof(struct ev_idle))))
88 | LOG_CRITICAL("out of mem");
89 | if (0 != pidfile_write("mqtthttpd.pid"))
90 | LOG_CRITICAL("pidfile write mqtthttpd.pid failed");
91 |
92 | ev_timer_init(g_srvctx.timer_1hz, (void *)timeout_1hz_cb, 0, 1);
93 | ev_set_priority(g_srvctx.timer_1hz, EV_MAXPRI);
94 | ev_timer_start(g_srvctx.loop, g_srvctx.timer_1hz);
95 | ev_timer_init(g_srvctx.timer_10hz, (void *)timeout_10hz_cb, 0, 0.1);
96 | ev_set_priority(g_srvctx.timer_10hz, EV_MAXPRI);
97 | ev_timer_start(g_srvctx.loop, g_srvctx.timer_10hz);
98 | ev_idle_init(g_srvctx.idle_watcher, idle_cb);
99 |
100 | if (0 != httpd_init(&(g_srvctx.httpd), g_srvctx.loop))
101 | LOG_CRITICAL("httpd_init failed");
102 | if (0 != mqtt_init(g_srvctx.loop, &(g_srvctx.mqttctx)))
103 | LOG_CRITICAL("mqtt_init failed");
104 |
105 | atexit(exitfunc);
106 |
107 | if (0 != parse_options(argc, argv))
108 | {
109 | usage();
110 | return 1;
111 | }
112 |
113 | // Start infinite loop
114 | while (1)
115 | {
116 | ev_loop(g_srvctx.loop, 0);
117 | }
118 |
119 | return 0;
120 | }
121 |
122 |
--------------------------------------------------------------------------------
/mqtthttpd.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef mqtthttpd_H
17 | #define mqtthttpd_H 1
18 |
19 | #include
20 |
21 | #include "mqtt.h"
22 | #include "httpd.h"
23 |
24 | struct server_context
25 | {
26 | struct ev_loop *loop;
27 |
28 | ev_timer *timer_1hz;
29 | ev_timer *timer_10hz;
30 | struct ev_idle *idle_watcher;
31 |
32 | mqtt_context_t mqttctx;
33 | char mqtt_server[512];
34 | uint16_t mqtt_port;
35 | char mqtt_name[256];
36 |
37 | ebb_server httpd;
38 | char *httpd_root;
39 | httpd_virt vhttpd[MAX_VHOST];
40 | uint32_t num_vhost;
41 |
42 | uint32_t httpdconnid; // global ids
43 | uint32_t streamid; // global ids
44 | };
45 | typedef struct server_context server_context_t;
46 |
47 |
48 | int mqtthttpd_listen(int port);
49 | void mqtthttpd_start_sleep(int t);
50 | int mqtthttpd_run_script(char *filename);
51 |
52 | #endif
53 |
54 |
--------------------------------------------------------------------------------
/msgcache.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include "msgcache.h"
19 | #include
20 |
21 | struct mc_node
22 | {
23 | RB_ENTRY(mc_node) entry;
24 | char *topic;
25 | char *msg;
26 | };
27 |
28 | static int mc_cmp(struct mc_node *e1, struct mc_node *e2)
29 | {
30 | return strcmp(e1->topic, e2->topic);
31 | }
32 |
33 | RB_HEAD(msgcache, mc_node) mc_head = RB_INITIALIZER(&mc_head);
34 | RB_PROTOTYPE(msgcache, mc_node, entry, mc_cmp);
35 | RB_GENERATE(msgcache, mc_node, entry, mc_cmp);
36 |
37 | const char *msgcache_lookup(char *topic)
38 | {
39 | struct mc_node key;
40 | key.topic = topic;
41 | if (NULL == RB_FIND(msgcache, &mc_head, &key))
42 | return NULL;
43 | return key.msg;
44 | }
45 |
46 | int msgcache_insert(char *topic, char *msg)
47 | {
48 | struct mc_node *found;
49 | struct mc_node *data = NULL;
50 | struct mc_node key;
51 |
52 | key.topic = topic;
53 | if (NULL != (found = RB_FIND(msgcache, &mc_head, &key)))
54 | {
55 | free(found->msg);
56 | if (NULL == (found->msg = strdup(msg)))
57 | return 1;
58 | else
59 | return 0;
60 | }
61 | else
62 | {
63 | if (NULL == (data = calloc(sizeof(struct mc_node), 1)))
64 | goto fail;
65 |
66 | if (NULL == (data->topic = strdup(topic)))
67 | goto fail;
68 | if (NULL == (data->msg = strdup(msg)))
69 | goto fail;
70 |
71 | if (NULL != RB_INSERT(msgcache, &mc_head, data)) // NULL means OK
72 | {
73 | LOG_CRITICAL("msgcache_insert mc collision"); // can't happen
74 | return 1;
75 | }
76 | }
77 | return 0;
78 |
79 | fail:
80 | if (NULL != data)
81 | {
82 | if (NULL != data->topic)
83 | free(topic);
84 | if (NULL != data->msg)
85 | free(msg);
86 | free(data);
87 | }
88 | return 1;
89 | }
90 |
91 | bool msgcache_remove(char *topic)
92 | {
93 | struct mc_node key;
94 | struct mc_node *res;
95 |
96 | key.topic = topic;
97 | res = RB_FIND(msgcache, &mc_head, &key);
98 | if (NULL == res)
99 | return false;
100 |
101 | RB_REMOVE(msgcache, &mc_head, res);
102 | free(res->topic);
103 | free(res->msg);
104 | free(res);
105 | return true;
106 | }
107 |
108 |
109 | void msgcache_foreach(msgcache_iterate_func f, void *userdata)
110 | {
111 | struct mc_node *i;
112 | RB_FOREACH(i, msgcache, &mc_head)
113 | {
114 | f(i->topic, i->msg, userdata);
115 | }
116 | }
117 |
118 |
119 |
--------------------------------------------------------------------------------
/msgcache.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef MSGCACHE_H
17 | #define MSGCACHE_H 1
18 |
19 | typedef void(*msgcache_iterate_func)(const char *topic, const char *msg, void *userdata);
20 |
21 | extern int msgcache_insert(char *topic, char *msg);
22 | extern bool msgcache_remove(char *topic);
23 | extern void msgcache_foreach(msgcache_iterate_func f, void *data);
24 | const char *msgcache_lookup(char *topic);
25 |
26 | #endif
27 |
28 |
--------------------------------------------------------------------------------
/opt.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 | #include "mqtthttpd.h"
30 | #include "mqtt.h"
31 | #include "httpd.h"
32 | #include "stream.h"
33 | #include "streamlist.h"
34 | #include "accesslog.h"
35 | #include "opt.h"
36 |
37 | static char *mqtt_server = NULL;
38 | static int mqtt_port = -1;
39 | static int httpd_port = -1;
40 | static char *httpd_root = NULL;
41 | static int verbose = 0;
42 | static char *client_name = NULL;
43 | static char *cert_file = NULL;
44 | static char *key_file = NULL;
45 |
46 | static struct option long_options[] =
47 | {
48 | {"help", no_argument, 0, 'h'},
49 | {"device", required_argument, 0, 'd'},
50 | {"server", required_argument, 0, 's'},
51 | {"port", required_argument, 0, 'p'},
52 | {"verbose", no_argument, 0, 'v'},
53 | {"name", required_argument, 0, 'n'},
54 | {"root", required_argument, 0, 'r'},
55 | {"listen", required_argument, 0, 'l'},
56 | {"sslcert", required_argument, 0, 'c'},
57 | {"sslkey", required_argument, 0, 'k'},
58 | {0, 0, 0, 0}
59 | };
60 |
61 | extern server_context_t g_srvctx;
62 |
63 | void usage(void)
64 | {
65 | fprintf(stderr, "mqtthttpd\n");
66 | fprintf(stderr, "mqtthttpd -v -r htdocs -l 8080 -n foo -s test.mosquitto.org -p 1883\n");
67 | fprintf(stderr, " --verbose -v Chatty mode, -vv for more\n");
68 | fprintf(stderr, " --server=test.mosquitto.org -s MQTT broker host\n");
69 | fprintf(stderr, " --port=1883 -p MQTT broker port\n");
70 | fprintf(stderr, " --name=mqttname -n MQTT client name\n");
71 | fprintf(stderr, " --listen=8080 -l HTTP server port\n");
72 | fprintf(stderr, " --root=htdocs -r HTTP server root\n");
73 | fprintf(stderr, " --sslcert=ca-cert.pem -c SSL certificate\n");
74 | fprintf(stderr, " --sslkey=ca-key.pem -k SSL key\n");
75 | }
76 |
77 | int parse_options(int argc, char **argv)
78 | {
79 | int c;
80 | int option_index;
81 |
82 | while(1)
83 | {
84 | c = getopt_long(argc, argv, "n:vhs:p:l:r:c:k:", long_options, &option_index);
85 | if (c == -1)
86 | break;
87 | switch(c)
88 | {
89 | case 'h':
90 | return 1;
91 | break;
92 | case 'c':
93 | cert_file = strdup(optarg);
94 | break;
95 | case 'k':
96 | key_file = strdup(optarg);
97 | break;
98 | case 'n':
99 | client_name = strdup(optarg);
100 | break;
101 | case 's':
102 | mqtt_server = strdup(optarg);
103 | break;
104 | case 'r':
105 | httpd_root = strdup(optarg);
106 | break;
107 | case 'p':
108 | mqtt_port = atoi(optarg);
109 | break;
110 | case 'l':
111 | httpd_port = atoi(optarg);
112 | break;
113 | case 'v':
114 | verbose++;
115 | break;
116 | default:
117 | return 1;
118 | break;
119 | }
120 | }
121 |
122 | if (NULL == client_name || NULL == mqtt_server || -1 == mqtt_port || -1 == httpd_port || NULL == httpd_root)
123 | return 1;
124 |
125 | if (verbose == 0)
126 | log_setlevel(LOGLEVEL_WARN);
127 | else
128 | if (verbose == 1)
129 | log_setlevel(LOGLEVEL_INFO);
130 | else
131 | if (verbose == 2)
132 | log_setlevel(LOGLEVEL_DEBUG);
133 |
134 | if (0 != mqtt_connect(&(g_srvctx.mqttctx), client_name, mqtt_server, mqtt_port))
135 | LOG_CRITICAL("Failed to connect to MQTT broker %s:%d", mqtt_server, mqtt_port);
136 |
137 | if (0 != httpd_listen(&(g_srvctx.httpd), httpd_root, httpd_port, cert_file, key_file))
138 | LOG_CRITICAL("Failed to start HTTP server on port %d, serving %s", httpd_port, httpd_root);
139 | return 0;
140 | }
141 |
142 |
143 |
144 |
--------------------------------------------------------------------------------
/opt.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef OPT_H
17 | #define OPT_H 1
18 |
19 | extern int parse_options(int argc, char **argv);
20 | extern void usage(void);
21 |
22 | #endif
23 |
24 |
--------------------------------------------------------------------------------
/stream.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include
19 |
20 | #include "mqtthttpd.h"
21 | #include "stream.h"
22 | #include "streamlist.h"
23 | #include "topic.h"
24 | #include "sublist.h"
25 |
26 | extern server_context_t g_srvctx;
27 |
28 | stream_t *stream_create(const char *topic_patt_str)
29 | {
30 | stream_t *stream;
31 |
32 | if (NULL == (stream = (stream_t *)calloc(sizeof(stream_t), 1)))
33 | goto fail;
34 | if (NULL == (stream->topic_patt = topic_create_from_string(topic_patt_str)))
35 | goto fail;
36 | if (NULL == (stream->rsp_q = tailq_create()))
37 | goto fail;
38 | if (NULL == (stream->topicstr = strdup(topic_patt_str)))
39 | goto fail;
40 | stream->timeoutS = DEFAULT_STREAM_TIMEOUT_S;
41 | stream->streamid = g_srvctx.streamid++;
42 |
43 | random_read(stream->seckey, STREAM_SECKEY_SIZE);
44 |
45 | return stream;
46 | fail:
47 | stream_destroy(stream);
48 | return NULL;
49 | }
50 |
51 | void stream_destroy(stream_t *stream)
52 | {
53 | if (NULL != stream)
54 | {
55 | while(!tailq_empty(stream->rsp_q))
56 | free(tailq_pop_head(stream->rsp_q));
57 | if (NULL != stream->topic_patt)
58 | topic_destroy(stream->topic_patt);
59 | if (stream->have_connid)
60 | httpd_timeout_conn(stream->connid);
61 | if (NULL != stream->topicstr)
62 | free(stream->topicstr);
63 | if (NULL != stream->rsp_q)
64 | tailq_destroy(stream->rsp_q);
65 | free(stream);
66 | }
67 | }
68 |
69 | int stream_push(stream_t *stream, char *str)
70 | {
71 | char *s;
72 | if (NULL == (s = strdup(str)))
73 | return 1;
74 | return tailq_push_tail(stream->rsp_q, s);
75 | }
76 |
77 | void stream_refresh_timer(stream_t *stream)
78 | {
79 | stream->timeoutS = DEFAULT_STREAM_TIMEOUT_S; // refresh timer
80 | }
81 |
82 | void stream_drain(stream_t *stream, stream_drain_func f, void *userdata)
83 | {
84 | bool first = true;
85 | while(!tailq_empty(stream->rsp_q))
86 | {
87 | char *s;
88 | s = (char *)tailq_pop_head(stream->rsp_q);
89 | f(stream, s, first, userdata);
90 | free(s);
91 | first = false;
92 | }
93 | stream_refresh_timer(stream);
94 | }
95 |
96 | bool stream_isempty(stream_t *stream)
97 | {
98 | return tailq_empty(stream->rsp_q);
99 | }
100 |
101 | void stream_set_connection(stream_t *stream, uint32_t connid)
102 | {
103 | if (stream->have_connid)
104 | LOG_DEBUG("stream hijacked, was connid=%u now %u", stream->connid, connid);
105 | stream->connid = connid;
106 | stream->have_connid = true;
107 | }
108 |
109 | bool stream_get_connection(stream_t *stream, uint32_t *connid)
110 | {
111 | if (stream->have_connid)
112 | {
113 | *connid = stream->connid;
114 | return true;
115 | }
116 | return false;
117 | }
118 |
119 | void stream_clear_connection(stream_t *stream)
120 | {
121 | stream->have_connid = false;
122 | }
123 |
124 | bool stream_1hz(uint32_t streamid, stream_t *stream, void *userdata)
125 | {
126 | if (0 == stream->timeoutS--)
127 | {
128 | LOG_DEBUG("streamid=%u timeout", streamid);
129 | if (sublist_remove(stream->topicstr))
130 | {
131 | LOG_INFO("unsubscribing from %s", stream->topicstr);
132 | mqtt_unsubscribe(&g_srvctx.mqttctx, stream->topicstr, NULL);
133 | }
134 | return true;
135 | }
136 | return false;
137 | }
138 |
139 |
--------------------------------------------------------------------------------
/stream.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef STREAM_H
17 | #define STREAM_H 1
18 |
19 | #include "topic.h"
20 | #include "tailq.h"
21 |
22 | #define DEFAULT_STREAM_TIMEOUT_S 30
23 | #define STREAM_SECKEY_SIZE 16
24 |
25 | typedef struct
26 | {
27 | uint32_t streamid;
28 | topic_node_t *topic_patt;
29 | char *topicstr;
30 | tailq_t *rsp_q;
31 | bool have_connid;
32 | uint32_t connid;
33 | uint32_t timeoutS;
34 | uint8_t seckey[STREAM_SECKEY_SIZE];
35 | } stream_t;
36 |
37 |
38 | typedef void(*stream_drain_func)(stream_t *stream, const char *str, bool first, void *userdata);
39 |
40 | extern stream_t *stream_create(const char *topic_patt_str);
41 | extern void stream_destroy(stream_t *stream);
42 | extern int stream_push(stream_t *stream, char *str);
43 | extern void stream_drain(stream_t *stream, stream_drain_func f, void *userdata);
44 | extern bool stream_isempty(stream_t *stream);
45 | extern void stream_set_connection(stream_t *stream, uint32_t connid);
46 | extern bool stream_get_connection(stream_t *stream, uint32_t *connid);
47 | extern void stream_clear_connection(stream_t *stream);
48 | extern bool stream_1hz(uint32_t streamid, stream_t *s, void *userdata); // return true to destroy
49 | extern void stream_refresh_timer(stream_t *stream);
50 |
51 |
52 | #endif
53 |
--------------------------------------------------------------------------------
/streamlist.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include "stream.h"
19 | #include "streamlist.h"
20 | #include
21 |
22 | struct stream_node
23 | {
24 | RB_ENTRY(stream_node) entry;
25 | uint32_t streamid;
26 | stream_t *stream;
27 | };
28 |
29 | static int stream_cmp(struct stream_node *e1, struct stream_node *e2)
30 | {
31 | return (e1->streamid < e2->streamid ? -1 : e1->streamid > e2->streamid);
32 | }
33 |
34 | RB_HEAD(streamlist, stream_node) stream_head = RB_INITIALIZER(&stream_head);
35 | RB_PROTOTYPE(streamlist, stream_node, entry, stream_cmp);
36 | RB_GENERATE(streamlist, stream_node, entry, stream_cmp);
37 |
38 | int streamlist_insert(uint32_t streamid, stream_t *stream)
39 | {
40 | struct stream_node *data;
41 |
42 | if (NULL == (data = malloc(sizeof(struct stream_node))))
43 | return 1;
44 |
45 | data->streamid = streamid;
46 | data->stream = stream;
47 |
48 | if (NULL == RB_INSERT(streamlist, &stream_head, data)) // NULL means OK
49 | {
50 | return 0;
51 | }
52 | else
53 | {
54 | LOG_CRITICAL("streamlist_insert streamid collision"); // can't happen
55 | return 1;
56 | }
57 |
58 | }
59 |
60 | stream_t *streamlist_remove(uint32_t streamid)
61 | {
62 | struct stream_node key;
63 | struct stream_node *res;
64 | stream_t *stream;
65 |
66 | key.streamid = streamid;
67 | res = RB_FIND(streamlist, &stream_head, &key);
68 | if (NULL == res)
69 | return NULL;
70 | RB_REMOVE(streamlist, &stream_head, res);
71 | stream = res->stream;
72 | free(res);
73 |
74 | return stream;
75 | }
76 |
77 | stream_t *streamlist_find(uint32_t streamid)
78 | {
79 | struct stream_node key;
80 | struct stream_node *res;
81 |
82 | key.streamid = streamid;
83 | res = RB_FIND(streamlist, &stream_head, &key);
84 | if (NULL == res)
85 | return NULL;
86 | return res->stream;
87 | }
88 |
89 | void streamlist_foreach(streamlist_iterate_func f, void *userdata)
90 | {
91 | struct stream_node *var, *nxt;
92 | // can't use foreach if we want to allow deletion
93 | for (var = RB_MIN(streamlist, &stream_head); var != NULL; var = nxt)
94 | {
95 | nxt = RB_NEXT(streamlist, &stream_head, var);
96 | if (f(var->streamid, var->stream, userdata))
97 | {
98 | RB_REMOVE(streamlist, &stream_head, var);
99 | if (0 != streamlist_remove(var->streamid))
100 | stream_destroy(var->stream);
101 | free(var);
102 | }
103 | }
104 | }
105 |
106 |
107 |
--------------------------------------------------------------------------------
/streamlist.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef STREAMLIST_H
17 | #define STREAMLIST_H 1
18 |
19 | #include "stream.h"
20 |
21 | typedef bool (*streamlist_iterate_func)(uint32_t streamid, stream_t *s, void *userdata); // true deletes it
22 |
23 | extern int streamlist_insert(uint32_t streamid, stream_t *s);
24 | extern stream_t *streamlist_remove(uint32_t streamid);
25 | extern stream_t *streamlist_find(uint32_t streamid);
26 | extern void streamlist_foreach(streamlist_iterate_func f, void *data);
27 |
28 | #endif
29 |
30 |
--------------------------------------------------------------------------------
/sublist.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include "sublist.h"
19 | #include
20 |
21 | struct sub_node
22 | {
23 | RB_ENTRY(sub_node) entry;
24 | char *topic;
25 | uint32_t count;
26 | };
27 |
28 | static int sub_cmp(struct sub_node *e1, struct sub_node *e2)
29 | {
30 | return strcmp(e1->topic, e2->topic);
31 | }
32 |
33 | RB_HEAD(sublist, sub_node) sub_head = RB_INITIALIZER(&sub_head);
34 | RB_PROTOTYPE(sublist, sub_node, entry, sub_cmp);
35 | RB_GENERATE(sublist, sub_node, entry, sub_cmp);
36 |
37 | static struct sub_node *sublist_find(char *topic)
38 | {
39 | struct sub_node key;
40 | key.topic = topic;
41 | return RB_FIND(sublist, &sub_head, &key);
42 | }
43 |
44 | bool sublist_insert(char *topic)
45 | {
46 | struct sub_node *found;
47 |
48 | if (NULL != (found = sublist_find(topic)))
49 | {
50 | found->count++;
51 | return false;
52 | }
53 | else
54 | {
55 | struct sub_node *data;
56 |
57 | if (NULL == (data = malloc(sizeof(struct sub_node))))
58 | return false;
59 |
60 | if (NULL == (data->topic = strdup(topic)))
61 | {
62 | free(data);
63 | return false;
64 | }
65 | data->count = 1;
66 |
67 | if (NULL == RB_INSERT(sublist, &sub_head, data)) // NULL means OK
68 | return true;
69 | else
70 | {
71 | LOG_CRITICAL("sublist_insert sub collision"); // can't happen
72 | return false;
73 | }
74 | }
75 | }
76 |
77 | bool sublist_remove(char *topic)
78 | {
79 | struct sub_node key;
80 | struct sub_node *res;
81 |
82 | key.topic = topic;
83 | res = RB_FIND(sublist, &sub_head, &key);
84 | if (NULL == res)
85 | return false;
86 |
87 | if (--res->count == 0)
88 | {
89 | RB_REMOVE(sublist, &sub_head, res);
90 | free(res->topic);
91 | free(res);
92 | return true;
93 | }
94 |
95 | return false;
96 | }
97 |
98 |
99 | void sublist_foreach(sublist_iterate_func f, void *userdata)
100 | {
101 | struct sub_node *i;
102 | RB_FOREACH(i, sublist, &sub_head)
103 | {
104 | f(i->topic, i->count, userdata);
105 | }
106 | }
107 |
108 |
109 |
--------------------------------------------------------------------------------
/sublist.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef SUBLIST_H
17 | #define SUBLIST_H 1
18 |
19 | typedef void(*sublist_iterate_func)(const char *topic, uint32_t count, void *userdata);
20 |
21 | extern bool sublist_insert(char *topic);
22 | extern bool sublist_remove(char *topic);
23 | extern void sublist_foreach(sublist_iterate_func f, void *data);
24 |
25 | #endif
26 |
27 |
--------------------------------------------------------------------------------
/tailq.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #include "tailq.h"
22 |
23 | void tailq_destroy(tailq_t *tq)
24 | {
25 | if (NULL != tq)
26 | {
27 | struct tailq_node *n1;
28 | struct tailq_node *n2;
29 |
30 | n1 = TAILQ_FIRST(&tq->root);
31 | while (n1 != NULL)
32 | {
33 | n2 = TAILQ_NEXT(n1, entry);
34 | free(n1);
35 | n1 = n2;
36 | }
37 |
38 | free(tq);
39 | }
40 | }
41 |
42 | tailq_t *tailq_create(void)
43 | {
44 | tailq_t *tq;
45 |
46 | if (NULL == (tq = (tailq_t *)calloc(sizeof(tailq_t), 1)))
47 | goto fail;
48 |
49 | TAILQ_INIT(&tq->root);
50 |
51 | return tq;
52 | fail:
53 | tailq_destroy(tq);
54 | return NULL;
55 | }
56 |
57 | int tailq_push_head(tailq_t *tq, void *userdata)
58 | {
59 | struct tailq_node *n1;
60 | LOG_DEBUG("tailq_push_head %p %p", tq, userdata);
61 | if (NULL == (n1 = calloc(sizeof(struct tailq_node), 1)))
62 | return 1;
63 | n1->userdata = userdata;
64 | TAILQ_INSERT_HEAD(&tq->root, n1, entry);
65 | return 0;
66 | }
67 |
68 | int tailq_push_tail(tailq_t *tq, void *userdata)
69 | {
70 | struct tailq_node *n1;
71 | LOG_DEBUG("tailq_push_tail %p %p", tq, userdata);
72 | if (NULL == (n1 = calloc(sizeof(struct tailq_node), 1)))
73 | return 1;
74 | n1->userdata = userdata;
75 | TAILQ_INSERT_TAIL(&tq->root, n1, entry);
76 | return 0;
77 | }
78 |
79 | void *tailq_pop_head(tailq_t *tq)
80 | {
81 | struct tailq_node *n1;
82 | void *userdata;
83 | if (NULL == (n1 = TAILQ_FIRST(&tq->root)))
84 | return NULL;
85 | userdata = n1->userdata;
86 | LOG_DEBUG("tailq_pop_head %p %p", tq, userdata);
87 | TAILQ_REMOVE(&tq->root, n1, entry);
88 | free(n1);
89 | return userdata;
90 | }
91 |
92 | void *tailq_pop_tail(tailq_t *tq)
93 | {
94 | struct tailq_node *n1;
95 | void *userdata;
96 | if (NULL == (n1 = TAILQ_LAST(&tq->root, tailq)))
97 | return NULL;
98 | userdata = n1->userdata;
99 | LOG_DEBUG("tailq_pop_tail %p %p", tq, userdata);
100 | TAILQ_REMOVE(&tq->root, n1, entry);
101 | free(n1);
102 | return userdata;
103 | }
104 |
105 | bool tailq_empty(tailq_t *tq)
106 | {
107 | return TAILQ_EMPTY(&tq->root);
108 | }
109 |
110 | #if 0
111 | void tailq_dump(tailq_t *tq)
112 | {
113 | struct tailq_node *item;
114 | size_t sz;
115 |
116 | TAILQ_FOREACH(item, &tq->root, entry)
117 | {
118 | memcpy(&sz, item->userdata, sizeof(size_t));
119 | LOG_DEBUG("%p %u", item->userdata, sz);
120 | if (sz > 1024 && tq->magic)
121 | {
122 | crash();
123 | }
124 | }
125 | }
126 | #endif
127 |
--------------------------------------------------------------------------------
/tailq.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef TAILQ_H
17 | #define TAILQ_H 1
18 |
19 | #include
20 |
21 | struct tailq_node
22 | {
23 | TAILQ_ENTRY(tailq_node) entry;
24 | void *userdata;
25 | };
26 |
27 | typedef struct
28 | {
29 | TAILQ_HEAD(tailq, tailq_node) root;
30 | bool magic;
31 | } tailq_t;
32 |
33 | extern tailq_t *tailq_create(void);
34 | extern void tailq_destroy(tailq_t *tq);
35 |
36 | extern int tailq_push_head(tailq_t *tq, void *userdata);
37 | extern int tailq_push_tail(tailq_t *tq, void *userdata);
38 |
39 | extern void *tailq_pop_head(tailq_t *tq);
40 | extern void *tailq_pop_tail(tailq_t *tq);
41 |
42 | extern bool tailq_empty(tailq_t *tq);
43 | extern void tailq_dump(tailq_t *tq);
44 |
45 | #endif
46 |
47 |
--------------------------------------------------------------------------------
/topic.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 |
19 | #include "topic.h"
20 |
21 |
22 | topic_node_t *topic_create(void)
23 | {
24 | topic_node_t *tn;
25 | tn = (topic_node_t *)malloc(sizeof(topic_node_t));
26 | tn->next = NULL;
27 | tn->str = NULL;
28 | return tn;
29 | }
30 |
31 | void topic_destroy(topic_node_t *tn)
32 | {
33 | while(NULL != tn)
34 | {
35 | topic_node_t *next;
36 | next = tn->next;
37 | if (NULL != tn->str)
38 | free(tn->str);
39 | free(tn);
40 | tn = next;
41 | }
42 | }
43 |
44 | void topic_print(topic_node_t *root)
45 | {
46 | while(NULL != root)
47 | {
48 | switch(root->type)
49 | {
50 | case TOPIC_NODE_TYPE_STR:
51 | LOG_USER_NOLN("%s", root->str);
52 | break;
53 | case TOPIC_NODE_TYPE_PLUS:
54 | LOG_USER_NOLN("+");
55 | break;
56 | case TOPIC_NODE_TYPE_HASH:
57 | LOG_USER_NOLN("#");
58 | break;
59 | }
60 | if (NULL != root->next)
61 | LOG_USER_NOLN("/");
62 | root = root->next;
63 | }
64 | LOG_USER("");
65 | }
66 |
67 | bool topic_match_string(topic_node_t *rule, const char *topicstr)
68 | {
69 | const char *elem_start;
70 | size_t len;
71 |
72 | elem_start = topicstr;
73 |
74 | while(1)
75 | {
76 | if (NULL == rule)
77 | return false;
78 |
79 | if (*topicstr == '/' || *topicstr == 0)
80 | {
81 | switch(rule->type)
82 | {
83 | case TOPIC_NODE_TYPE_STR:
84 | len = strlen(rule->str);
85 | if (len != (topicstr - elem_start))
86 | return false;
87 | if (0!=memcmp(elem_start, rule->str, len))
88 | return false;
89 | break;
90 | case TOPIC_NODE_TYPE_PLUS:
91 | // matches
92 | break;
93 | case TOPIC_NODE_TYPE_HASH:
94 | return true;
95 | break;
96 | }
97 |
98 | rule = rule->next;
99 | elem_start = topicstr+1;
100 | }
101 |
102 | if (0 == *topicstr++)
103 | break;
104 | }
105 |
106 | return true;
107 | }
108 |
109 | topic_node_t *topic_create_from_string(const char *topicstr)
110 | {
111 | topic_node_t *root = NULL;
112 | topic_node_t *p = NULL;
113 | bool seen_hash = false;
114 | const char *elem_start;
115 |
116 | elem_start = topicstr;
117 |
118 | while(1)
119 | {
120 | if (*topicstr == '/' || *topicstr == 0)
121 | {
122 | if (seen_hash) // hash must be last item
123 | {
124 | LOG_ERROR("Hierarchy should end at #");
125 | goto fail;
126 | }
127 |
128 | if (NULL == root)
129 | {
130 | if (NULL == (root = topic_create()))
131 | {
132 | LOG_ERROR("create root failed");
133 | goto fail;
134 | }
135 | p = root;
136 | }
137 | else
138 | {
139 | if (NULL == (p->next = topic_create()))
140 | {
141 | LOG_ERROR("create child failed");
142 | goto fail;
143 | }
144 | p = p->next;
145 | }
146 |
147 | if ((1 == topicstr - elem_start) && *elem_start == '+')
148 | p->type = TOPIC_NODE_TYPE_PLUS;
149 | else
150 | if ((1 == topicstr - elem_start) && *elem_start == '#')
151 | p->type = TOPIC_NODE_TYPE_HASH;
152 | else
153 | {
154 | p->type = TOPIC_NODE_TYPE_STR;
155 | if (NULL == (p->str = (char *)malloc((topicstr - elem_start)+1)))
156 | {
157 | LOG_ERROR("string alloc");
158 | goto fail;
159 | }
160 | memcpy(p->str, elem_start, topicstr - elem_start);
161 | p->str[topicstr - elem_start] = 0;
162 | }
163 |
164 |
165 | elem_start = topicstr+1;
166 | }
167 |
168 | if (0 == *topicstr++)
169 | break;
170 | }
171 |
172 | return root;
173 | fail:
174 | topic_destroy(root);
175 | return NULL;
176 | }
177 |
178 | #if 0
179 | int topic_test(void)
180 | {
181 | topic_node_t *tn;
182 | const char *topic = "a/b/c/d";
183 | const char *good_rules[] = {
184 | "a/b/c/d", "+/b/c/d", "a/+/c/d", "a/+/+/d", "+/+/+/+",
185 | "#", "a/#", "a/b/#", "a/b/c/#", "+/b/c/#"
186 | };
187 | const char *bad_rules[] = {
188 | "a/b/c", "b/+/c/d", "+/+/",
189 | "b/#", "/#", "/", ""
190 | };
191 | int good_rules_len = sizeof(good_rules) / sizeof(char *);
192 | int bad_rules_len = sizeof(bad_rules) / sizeof(char *);
193 | int i;
194 |
195 | for (i=0;i
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef TOPIC_H
17 | #define TOPIC_H 1
18 |
19 | typedef enum
20 | {
21 | TOPIC_NODE_TYPE_STR,
22 | TOPIC_NODE_TYPE_PLUS,
23 | TOPIC_NODE_TYPE_HASH
24 | } topic_node_type_t;
25 |
26 | struct topic_node
27 | {
28 | topic_node_type_t type;
29 | char *str;
30 | struct topic_node *next;
31 | };
32 | typedef struct topic_node topic_node_t;
33 |
34 |
35 | extern topic_node_t *topic_create(void);
36 | extern void topic_destroy(topic_node_t *tn);
37 | extern void topic_print(topic_node_t *root);
38 | extern bool topic_match_string(topic_node_t *rule, const char *topicstr);
39 | extern topic_node_t *topic_create_from_string(const char *topicstr);
40 |
41 | #endif
42 |
43 |
--------------------------------------------------------------------------------
/webapi.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef WEBAPI_H
17 | #define WEBAPI_H 1
18 |
19 | void webapi_publish_cb(int mid);
20 | void webapi_subscribe_cb(int mid);
21 | void webapi_message_cb(int mid, char *topic, char *msg);
22 | void webapi_mqtt_failall_cb(void);
23 |
24 | #endif
25 |
26 |
--------------------------------------------------------------------------------
/webapi_serve.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include "mqtthttpd.h"
22 | #include "httpd.h"
23 | #include "webapi.h"
24 | #include "webapi_serve.h"
25 | #include "httpdconnlist.h"
26 | #include "accesslog.h"
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 |
35 | #include
36 |
37 | #define DEFAULT_FILENAME "index.html"
38 |
39 | extern server_context_t g_srvctx;
40 |
41 | /*************** mimetype handling */
42 | typedef struct
43 | {
44 | const char *ext;
45 | const char *mime;
46 | } filext_t;
47 |
48 | static filext_t extensions[] =
49 | {
50 | {".html", "text/html"},
51 | {".js", "application/x-javascript"},
52 | {".css", "text/css"},
53 | {".gif", "image/gif"},
54 | {".png", "image/png"},
55 | {".svg", "image/svg+xml"},
56 | {".mpg", "video/mpeg"},
57 | {".c", "text/plain"},
58 | {".h", "text/plain"},
59 | {".gz", "text/plain"},
60 | {".zip", "text/plain"},
61 | {NULL, "text/plain"}
62 | };
63 |
64 | static const char *lookup_mimetype(const char *filename)
65 | {
66 | filext_t *p = extensions;
67 | while(p->ext != NULL)
68 | {
69 | if (NULL != strcasestr(filename, p->ext))
70 | break;
71 | p++;
72 | }
73 | return p->mime;
74 | }
75 | /************************************/
76 |
77 | int webapi_serve(uint32_t connid, uint32_t method, int argc, char **argv, const char *body, const char *reqpath, int vhost)
78 | {
79 | int fd = -1;
80 | size_t count;
81 | ebb_connection *conn;
82 | ebb_connection_info *conninfo = NULL;
83 | struct stat st;
84 | char path[PATH_MAX];
85 | uint8_t buf[512];
86 | const char *rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT";
87 | char reqpath_fname[4096];
88 |
89 | LOG_DEBUG("webapi_serve %s", reqpath);
90 |
91 | if (strlen(reqpath) > sizeof(reqpath_fname)/2) // FIXME
92 | {
93 | LOG_INFO("path too long");
94 | return 1;
95 | }
96 | if (0 != uriUriStringToUnixFilenameA(reqpath, reqpath_fname))
97 | {
98 | LOG_INFO("bad req %s", reqpath);
99 | return 1;
100 | }
101 |
102 | reqpath = reqpath_fname;
103 |
104 | if (NULL == (conn = httpdconnlist_find(connid)))
105 | {
106 | LOG_WARN("no such connid %d", connid);
107 | goto fail;
108 | }
109 | conninfo = (ebb_connection_info *)(conn->data);
110 | if (NULL == conninfo)
111 | goto fail;
112 |
113 | if (method != EBB_GET)
114 | goto fail;
115 |
116 | if (NULL == reqpath || reqpath[0] == 0)
117 | reqpath = "/";
118 |
119 | if (vhost < 0)
120 | {
121 | snprintf(path, sizeof(path), "%s%s", g_srvctx.httpd_root, reqpath);
122 | if (!path_in_dir(g_srvctx.httpd_root, path))
123 | {
124 | LOG_DEBUG("request for %s out of root (%s)", path, g_srvctx.httpd_root);
125 | goto fail;
126 | }
127 | }
128 | else
129 | {
130 | snprintf(path, sizeof(path), "%s%s", g_srvctx.vhttpd[vhost].rootdir, reqpath);
131 | if (!path_in_dir(g_srvctx.vhttpd[vhost].rootdir, path))
132 | {
133 | LOG_DEBUG("request for %s out of root (%s)", path, g_srvctx.vhttpd[vhost].rootdir);
134 | goto fail;
135 | }
136 | }
137 |
138 | if ((fd = open(path, O_RDONLY)) < 0)
139 | goto fail;
140 | if (fstat(fd, &st) < 0)
141 | goto fail;
142 |
143 | if (S_ISDIR(st.st_mode))
144 | {
145 | char path2[PATH_MAX];
146 | strcpy(path2, path);
147 | snprintf(path, sizeof(path), "%s%s", path2, DEFAULT_FILENAME);
148 | close(fd);
149 | fd = -1;
150 | if (vhost < 0)
151 | {
152 | if (!path_in_dir(g_srvctx.httpd_root, path))
153 | {
154 | LOG_DEBUG("request for %s out of root", path);
155 | goto fail;
156 | }
157 | }
158 | else
159 | {
160 | if (!path_in_dir(g_srvctx.vhttpd[vhost].rootdir, path))
161 | {
162 | LOG_DEBUG("request for %s out of root", path);
163 | goto fail;
164 | }
165 | }
166 | if ((fd = open(path, O_RDONLY)) < 0)
167 | goto fail;
168 | if (fstat(fd, &st) < 0)
169 | goto fail;
170 | }
171 |
172 | if(!S_ISREG(st.st_mode))
173 | goto fail;
174 |
175 | strcpy(conninfo->mimetype, lookup_mimetype(path));
176 |
177 | // fill out a last-modified header
178 | strftime( conninfo->last_modified, sizeof(conninfo->last_modified), rfc1123fmt, localtime( &st.st_mtime ) );
179 |
180 | if (st.st_mtime <= conninfo->ifmodifiedsince && conninfo->ifmodifiedsince != 0)
181 | {
182 | // LOG_INFO("not modified since ifms=%u mtime=%u", conninfo->ifmodifiedsince, st.st_mtime);
183 | conninfo->http_status = 304; // not modified
184 | LOG_DEBUG("not modified %s", path);
185 | httpd_close(connid);
186 | if (fd != 1)
187 | close(fd);
188 | return 0;
189 | }
190 |
191 | while((count = read(fd, buf, sizeof(buf)-1)) > 0)
192 | httpd_pushbin(connid, buf, count, false);
193 |
194 | httpd_close(connid);
195 | LOG_DEBUG("served %s", path);
196 |
197 | accesslog_write("%s %s %s", conninfo->ipaddr, path, reqpath);
198 |
199 | if (fd != -1)
200 | close(fd);
201 | return 0;
202 | fail:
203 | if (fd != -1)
204 | close(fd);
205 | if (NULL != conninfo)
206 | conninfo->http_status = 404; // not found
207 | return 1;
208 | }
209 |
210 |
211 |
--------------------------------------------------------------------------------
/webapi_serve.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012, Toby Jaffey
3 | *
4 | * Permission to use, copy, modify, and distribute this software for any
5 | * purpose with or without fee is hereby granted, provided that the above
6 | * copyright notice and this permission notice appear in all copies.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | #ifndef WEBAPI_SERVE_H
17 | #define WEBAPI_SERVE_H 1
18 | int webapi_serve(uint32_t connid, uint32_t method, int argc, char **argv, const char *body, const char *reqpath, int vhost);
19 | #endif
20 |
21 |
--------------------------------------------------------------------------------