31 | #endif
32 |
33 | /* Colors. Why not ? */
34 | #define KNRM "\x1B[0m"
35 | #define KRED "\x1B[31m"
36 | #define KGRN "\x1B[32m"
37 | #define KYEL "\x1B[33m"
38 | #define KBLU "\x1B[34m"
39 | #define KMAG "\x1B[35m"
40 | #define KCYN "\x1B[36m"
41 | #define KWHT "\x1B[37m"
42 | #define RESET "\033[0m"
43 |
44 | /* Define boolean */
45 | typedef int bool;
46 | #define true 1
47 | #define false 0
48 |
49 | #define SERVER_STRING "Server: wafer.chttpd/0.1.0\r\n"
50 | #define ToHex(Y) (Y>='0'&&Y<='9'?Y-'0':Y-'A'+10)
51 | #define UNDEFINED "VALUE_UNDEFINED"
52 |
53 | /* Settings */
54 | #define WAFER_ONE_K 1024
55 | #define MAX_HEADERS 1024
56 | #define MAX_BUFFER_SIZE 8192
57 | #define MAX_DPRINTF_SIZE 64
58 | #ifdef WAFER_MAX_CON_CONS
59 | #define MAX_NO_FDS WAFER_MAX_CON_CONS
60 | #else
61 | #define MAX_NO_FDS 1024
62 | #endif
63 | #define MAX_METHOD_SIZE 32
64 | #define MAX_VER_SIZE 32
65 | #define MAX_REQUEST_SIZE 8192
66 | #define MAX_EVENTS MAX_NO_FDS/2
67 | #define POLL_TIMEOUT 100 /* ms */
68 | /* Define HTTP request parsing states */
69 | #define STATE_PRE_REQUEST 0
70 | #define STATE_METHOD 1
71 | #define STATE_URI 2
72 | #define STATE_VERSION 3
73 | #define STATE_HEADER 4
74 | #define STATE_COMPLETE_READING 5
75 |
76 | #define STATUS_HTTP_OK 200
77 | #define STATUS_HTTP_NOT_FOUND 404
78 |
79 | typedef struct struct_request {
80 | char *reqStr;
81 | size_t reqStrLen;
82 | char *method;
83 | size_t methodLen;
84 | char *ver;
85 | size_t verLen;
86 | char **headers;
87 | size_t headersLen;
88 | } Request;
89 |
90 | typedef struct struct_response {
91 | int fd;
92 | char **headers;
93 | int flags;
94 | int apiFlags;
95 | int status;
96 | } Response;
97 |
98 | typedef struct {
99 | short int state;
100 | char *readBuffer;
101 | short readBufferIdx;
102 | short readBufferLen;
103 | char *method;
104 | short methodIdx;
105 | char *uri;
106 | short uriIdx;
107 | char *ver;
108 | short verIdx;
109 | char **headers;
110 | short headersIdx;
111 | short withinHeaderIdx;
112 | } FdData;
113 |
114 | #define LISTENQ 1024 /* second argument to listen() */
115 | #define MAXLINE 1024 /* max length of a line */
116 | #define RIO_BUFSIZE 1024
117 |
118 | #define DEFAULT_PORT 4242
119 | #define DEFAULT_N_CHILDREN 0
120 |
121 | /*Preprocessor abuse */
122 | #define SIZE_OF_CHAR sizeof(char)
123 | #define LOG_ERROR_ON(_statement_,_condition_,_message_) do { if ((_statement_)==_condition_) fprintf(stderr,_message_); } while(0)
124 | #define LOG_ERROR_ON_NULL(_statement_,_message_) LOG_ERROR_ON(_statement_,NULL,_message_)
125 | #define NEW(T,v) do { T * v = malloc(sizeof(T)); } while (0)
126 | #define WAFER_STR(X) #X
127 | /* Globals */
128 | int default_port;
129 |
130 | /*Thread stuff!*/
131 | #ifdef WAFER_THREADS
132 | #define QUEUESIZE MAX_NO_FDS
133 |
134 | /* select_loop stuff */
135 | typedef struct {
136 | Request request;
137 | Response response;
138 | int fd;
139 | FdData *fdDataList;
140 | fd_set *pMaster;
141 | } THREAD_DATA;
142 |
143 | typedef struct {
144 | #ifdef WAFER_MAX_CON_CONS
145 | THREAD_DATA *buf;
146 | #else
147 | THREAD_DATA buf[QUEUESIZE];
148 | #endif
149 | long head, tail;
150 | int full, empty;
151 | pthread_mutex_t *mut;
152 | pthread_cond_t *notFull, *notEmpty;
153 | } queue;
154 |
155 | pthread_mutex_t *fd_mutex;
156 |
157 | /*Globals*/
158 | queue *fifo;
159 | queue *cleaner_fifo;
160 | int socketpair_fd[2];
161 |
162 | /*Functions*/
163 | void farmer_thread(THREAD_DATA);
164 | void *worker_thread(void *arg);
165 | void cleaner_thread(void);
166 | queue *queueInit(void);
167 | void queueDelete(queue * q);
168 | void queueAdd(queue * q, THREAD_DATA in);
169 | void queueDel(queue * q, THREAD_DATA * out);
170 | #endif
171 |
172 | void freeHeaders(char **);
173 | long dbgprintf(const char *format, ...);
174 |
175 | void server(Request * request, Response * response);
176 |
177 | #endif /* WAFER_H_ */
178 |
--------------------------------------------------------------------------------
/waferapi.c:
--------------------------------------------------------------------------------
1 | #include "wafer.h"
2 | #include "waferapi.h"
3 |
4 | /* Ver 0.0.5 Functions
5 | * Parameters: request, format, (optional) variadargs
6 | * Returns: the number of characters printed
7 | */
8 |
9 | /*Internal functions don't use */
10 | ssize_t writeLongString(int client, const char *longString, size_t len);
11 | static void sendFileWithSelect(int client, int read_fd, struct stat stat_buf);
12 | #ifndef __linux__
13 | sendfile_wafer (int write_fd, int read_fd, off_t *offset,int remain);
14 | #endif
15 | char * getBufferSpace ();
16 | /*End internal functions */
17 |
18 | long resPrintf(Response * response, const char *format, ...)
19 | {
20 | dbgprintf("Flags %d API Flags% d response.status %d\n",response->flags,response->apiFlags,response->status);
21 | if (!(response->apiFlags&API_FLAGS_HEADER_SENT)) {
22 | if (!(response->apiFlags&API_FLAGS_DONT_SET_HEADER_BEFORE_SENDING)) {
23 | response->apiFlags|=API_FLAGS_HEADER_SENT;
24 | sendStatusOKHeadersTypeEncoding(response,"text/html",NULL);
25 | } else {
26 |
27 | /*sendHeadersResponse*/
28 | }
29 | }
30 | /* initial buffer large enough for most cases, will resize if required */
31 | char *buf = getBufferSpace();
32 | int len;
33 |
34 | va_list arg;
35 | long done;
36 | va_start(arg, format);
37 | len = vsnprintf(buf, MAX_BUFFER_SIZE, format, arg);
38 | va_end(arg);
39 |
40 | if (len > MAX_BUFFER_SIZE) {
41 | /* buffer size was not enough */
42 | free(buf);
43 | buf = malloc(len + 1);
44 | if (buf == NULL) {
45 | printf("Could not allocate memory.");
46 | exit(EXIT_FAILURE);
47 | }
48 | va_start(arg, format);
49 | vsnprintf(buf, len + 1, format, arg);
50 | va_end(arg);
51 | }
52 |
53 | if (len < MAX_BUFFER_SIZE) {
54 | done = (int)send(response->fd, buf, len, 0);
55 | } else {
56 | done = writeLongString(response->fd, buf, len);
57 | }
58 |
59 | free(buf);
60 | return done;
61 | }
62 |
63 | /*Writes the given null-terminated string to the compressed file, excluding the terminating null character.
64 | Returns the number of characters written, or -1 in case of error */
65 | long resPuts(Response * response, const char *buffer)
66 | {
67 | if (!(response->apiFlags&API_FLAGS_HEADER_SENT)) {
68 | if (!(response->apiFlags&API_FLAGS_DONT_SET_HEADER_BEFORE_SENDING)) {
69 | response->apiFlags|=API_FLAGS_HEADER_SENT;
70 | sendStatusOKHeadersTypeEncoding(response,"text/html",NULL);
71 | } else {
72 | /*sendHeadersResponse*/
73 | }
74 | }
75 | return send(response->fd, buffer, strlen(buffer), 0);
76 | }
77 |
78 | /* Serve file. */
79 | /* If displayFilename is not null, the file will be downloadable */
80 | void serveFile(Response * response, const char *filename, const char *displayFilename,
81 | const char *type)
82 | {
83 | FILE *pFile = NULL;
84 | response->apiFlags |= API_FLAGS_DONT_SET_HEADER_BEFORE_SENDING;
85 |
86 | pFile = fopen(filename, "r");
87 | if (pFile == NULL) {
88 | sendResourceNotFound(response);
89 | } else {
90 | int read_fd = fileno(pFile);
91 | struct stat stat_buf;
92 | fstat (read_fd, &stat_buf);
93 |
94 | STATIC_SEND(response->fd, "HTTP/1.0 200 OK\r\n");
95 | STATIC_SEND(response->fd, SERVER_STRING);
96 |
97 | resPrintf(response, "Content-Type: %s\r\n", type);
98 | resPrintf(response, "Content-Length: %d\r\n", stat_buf.st_size);
99 | if (displayFilename!=NULL) {
100 | resPrintf(response, "Content-Disposition: attachment; filename=\"%s\"\r\n",
101 | displayFilename);
102 | }
103 |
104 | STATIC_SEND(response->fd, "\r\n");
105 |
106 | sendFileWithSelect(response->fd, read_fd, stat_buf);
107 | }
108 | fclose(pFile);
109 | }
110 |
111 | void sendStatusOKHeadersTypeEncoding(Response * response, const char *type, const char *encoding)
112 | {
113 | STATIC_SEND(response->fd, "HTTP/1.0 200 OK\r\n");
114 | STATIC_SEND(response->fd, SERVER_STRING);
115 | resPrintf(response, "Content-Type: %s\r\n", type);
116 | if (encoding != NULL) {
117 | resPrintf(response, "Content-Encoding: %s\r\n", encoding);
118 | }
119 | STATIC_SEND(response->fd, "Vary: Accept-Encoding\r\n");
120 | STATIC_SEND(response->fd, "\r\n");
121 | response->status=STATUS_HTTP_OK;
122 | }
123 |
124 | void sendResourceNotFound(Response * response)
125 | {
126 | STATIC_SEND(response->fd, "HTTP/1.0 404 NOT FOUND\r\n");
127 | STATIC_SEND(response->fd, "Content-Type: text/html\r\n");
128 | STATIC_SEND(response->fd, "\r\n");
129 | STATIC_SEND(response->fd, "Not Found\r\n");
130 | STATIC_SEND(response->fd, "The server could not fulfill\r\n");
131 | STATIC_SEND(response->fd, "your request because the resource specified\r\n");
132 | STATIC_SEND(response->fd, "is unavailable or nonexistent.\r\n");
133 | STATIC_SEND(response->fd, "
\r\n");
134 | response->status=STATUS_HTTP_NOT_FOUND;
135 | }
136 |
137 | /*Input Functions */
138 | char *resQuickForm(Request *request, Response *response, const char *msg, const char *inputstr)
139 | {
140 | char *qpath = getQueryPath(request);
141 | char *qparam = getQueryParam(request,"q");
142 | if (!((API_FLAGS_FORM_ONLY_ON_NULL&response->apiFlags)&&qparam != NULL)) {
143 | resPrintf(response, OTAGA(form, action = "%s")
144 | "%s%s" STAG(input, type = "submit") CTAG(form), qpath, msg, inputstr);
145 | }
146 | free(qpath);
147 | return qparam;
148 | }
149 |
150 | /* Utility Functions */
151 | char *getHeader(char **headers, char *header)
152 | {
153 | char *current_header, *matching_header, *value;
154 | // Not sure if MAX_BUFFER_SIZE is right.
155 | char *retval = getBufferSpace();
156 |
157 | int i;
158 | for (i = 0; headers[i] != NULL; i++) {
159 | current_header = headers[i];
160 | if ((matching_header = strstr(current_header, header))) {
161 | value = matching_header + strlen(header);
162 | if (*value == ':') {
163 | while (*value == ' ' || *value == ':') {
164 | value++;
165 | }
166 | strncpy(retval, value, MAX_BUFFER_SIZE);
167 | return retval;
168 | }
169 | }
170 | }
171 | memcpy(retval, UNDEFINED, sizeof(UNDEFINED));
172 | return retval;
173 | }
174 |
175 | /**********************************************************************/
176 | /* Return the value of a query parameter.
177 | * Parameters: the query string
178 | * the name of the parameter
179 | * Returns: the value of the query parameter */
180 | /**********************************************************************/
181 |
182 | char *getQueryParam(Request *request, const char *name)
183 | {
184 | char * bufferAmpersand = getBufferSpace();;
185 | char * bufferQuestion = getBufferSpace();;
186 | snprintf(bufferQuestion, MAX_BUFFER_SIZE - 1, "?%s=", name);
187 | snprintf(bufferAmpersand, MAX_BUFFER_SIZE - 1, "&%s=", name);
188 | char *buffer;
189 | char *pos1;
190 |
191 | char *value = getBufferSpace();;
192 |
193 | int i;
194 |
195 | buffer = bufferQuestion;
196 | pos1 = strstr(request->reqStr, bufferQuestion);
197 | dbgprintf("Buffer %s Pos %s\n", buffer, pos1);
198 | if (!pos1) {
199 | buffer = bufferAmpersand;
200 | pos1 = strstr(request->reqStr, bufferAmpersand);
201 | }
202 | if (pos1) {
203 | pos1 += strlen(buffer);
204 | i = 0;
205 | while (*pos1 && *pos1 != '&' && i < MAX_BUFFER_SIZE-1) {
206 | if (*pos1 == '%') {
207 | value[i] = (char)ToHex(pos1[1]) * 16 + ToHex(pos1[2]);
208 | pos1 += 3;
209 | } else if (*pos1 == '+') {
210 | value[i] = ' ';
211 | pos1++;
212 | } else {
213 | value[i] = *pos1++;
214 | }
215 | i++;
216 | }
217 | value[i] = '\0';
218 | return value;
219 | }
220 |
221 | free(bufferAmpersand);
222 | free(bufferQuestion);
223 | free(value);
224 | value=NULL;
225 | return value;
226 | }
227 |
228 | /**********************************************************************/
229 | /* Return the query path. For example the query path for
230 | * http://waferdotc.com/faq is /faq
231 | * Note the / at the beginning
232 | * Parameters: the request
233 | * Returns: the query path */
234 | /**********************************************************************/
235 |
236 | char *getQueryPath(Request *request)
237 | {
238 | char *queryPath;
239 | queryPath = strdup(request->reqStr);
240 | u_int i;
241 | for (i = 0; i < strlen(request->reqStr) && (queryPath[i] != '?') && (queryPath[i] != '\0');
242 | i++) {
243 | }
244 |
245 | queryPath[i] = '\0';
246 |
247 | return queryPath;
248 | }
249 |
250 | bool routeRequest(Request *request, Response * response, const char *path, void (*function) (Request *, Response *))
251 | {
252 | char *queryPath = getQueryPath(request);
253 | if (strcmp(queryPath, path) == 0) {
254 | free(queryPath);
255 | if (function != NULL)
256 | function(request, response);
257 | return true;
258 | } else {
259 | free(queryPath);
260 | return false;
261 | }
262 | }
263 |
264 | /*Internal stuff follows. Could change in future. Do not use */
265 | char * getBufferSpace () {
266 | char *bufferSpace = malloc(MAX_BUFFER_SIZE * sizeof(char));
267 | if (bufferSpace == NULL) {
268 | fprintf(stderr, "Could not allocate memory.");
269 | exit(EXIT_FAILURE);
270 | }
271 | return bufferSpace;
272 | }
273 |
274 | #ifndef __linux__
275 | ssize_t sendfile_wafer (int write_fd, int read_fd, off_t *offset,int remain)
276 | {
277 | char * buf = getBufferSpace();
278 | lseek(read_fd, *offset, SEEK_SET);
279 | ssize_t bytes_read = read(read_fd, buf, MAX_BUFFER_SIZE);
280 | ssize_t bytes_written = write(write_fd, buf, bytes_read);
281 | free(buf);
282 | return bytes_written;
283 | }
284 | #endif // __linux__
285 |
286 | static void sendFileWithSelect(int write_fd, int read_fd, struct stat stat_buf)
287 | {
288 | off_t offset = 0;
289 |
290 | size_t remain = stat_buf.st_size;
291 | size_t sent = 0;
292 | fd_set write_fds;
293 | FD_ZERO(&write_fds);
294 | FD_SET(write_fd,&write_fds);
295 | struct timeval timeout;
296 | timeout.tv_sec = 5;
297 | timeout.tv_usec = 0;
298 |
299 | while (remain) {
300 | dbgprintf("Remain %d out of %d \n",remain,stat_buf.st_size);
301 | int selected = select (write_fd + 1, NULL, &write_fds, NULL, &timeout);
302 | if (selected < 0) {
303 | return; //TODO-Error Logging
304 | } else if (selected == 0) {
305 | dbgprintf("Timed out. Going back in \n");
306 | continue; //Timed out. Loop again.
307 | }
308 | #ifdef __linux__
309 | ssize_t sent_once = sendfile (write_fd, read_fd, &offset, remain);
310 | #else
311 | ssize_t sent_once = sendfile_wafer (write_fd, read_fd, &offset, remain);
312 | #endif // __linux__
313 | if (sent_once <= 0)
314 | return;
315 | sent += sent_once;
316 | offset = sent;
317 | remain -= sent_once;
318 | }
319 |
320 |
321 | /* Close up. */
322 | close (read_fd);
323 | }
324 |
325 | ssize_t writeLongString(int client, const char *longString, size_t len)
326 | {
327 | size_t remain = len;
328 | size_t sent = 0;
329 |
330 | while (remain) {
331 | size_t toCpy = remain > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : remain;
332 | ssize_t sent_once = send(client, longString, toCpy, 0);
333 | if (sent_once <= 0)
334 | return -1;
335 | sent += sent_once;
336 | longString += sent_once;
337 | remain -= sent_once;
338 | }
339 | return sent;
340 | }
341 |
--------------------------------------------------------------------------------
/waferapi.h:
--------------------------------------------------------------------------------
1 | #ifndef WAFERAPI_H_
2 | #define WAFERAPI_H_
3 | /* ENTL */
4 | #define STR(X) #X
5 | #define WSPC " "
6 | #define CRLF "\r\n"
7 | #define ATTR(key,value) STR(key) STR(=) STR(value)
8 |
9 | #define LT(tag) STR(<) STR(tag) WSPC /*) /*< */
12 |
13 | #define OTAG(tag) LT(tag) GT /* */
14 | #define OTAGA(tag,attributes) LTA(tag,attributes) GT /* */
15 | #define CTAG(tag) STR() STR(tag) GT /* */
16 |
17 | #define ESTAG(tag) LT(tag) WSPC STR(/) GT /* */
18 | #define STAG(tag,attributes) LTA(tag,attributes) WSPC STR(/) GT /* */
19 |
20 | #define QTAG(tag,text) OTAG(tag) text CTAG(tag) /* text */
21 | #define QTAGA(tag,attributes,text) OTAGA(tag,attributes) text CTAG(tag) /* text */
22 |
23 | /* Extensions */
24 | #define QLINK(url,text) QTAGA(a,href=url,text)
25 | #define QLINKA(url,attributes,text) QTAGA(a,href=url attributes,text)
26 | #define QIMG(srcurl) STAG(img,src=srcurl)
27 | #define QIMGA(srcurl) STAG(img,src=srcurl attributes)
28 | #define QBR ESTAG(br)
29 |
30 | #define STAGPARAMQ(tag,attributes) LTA(tag,attributes) ATTR(name,STR(q)) WSPC STR(/) GT /* */
31 | #define QTAGAPARAMQ(tag,attributes,text) OTAGA(tag,attributes name=STR(q)) text CTAG(tag) /* text */
32 | /* End ENTL */
33 |
34 | long resPrintf(Response *response, const char *format, ...);
35 | void serveFile(Response *response, const char *filename, const char *displayFilename,
36 | const char *type);
37 |
38 | void sendStatusOKHeadersTypeEncoding(Response *response, const char *type, const char *encoding);
39 | void sendResourceNotFound(Response *response);
40 |
41 | char *resQuickForm(Request *request, Response *response, const char *msg, const char *inputstr);
42 | #define QUICK_FORM_TEXT(request,response,msg) resQuickForm(request,response,msg,STAGPARAMQ(input,type="text"))
43 |
44 | char *getQueryParam(Request *request, const char *name);
45 | char *getQueryPath(Request *request);
46 | bool routeRequest(Request *request, Response * response, const char *path, void (*function) (Request *, Response *));
47 | long resPuts(Response * response, const char *buffer);
48 | /*Internal stuff follows. Could change in future. Do not use */
49 | #define STATIC_SEND(_socket, _str) send(_socket, _str, sizeof(_str)-1, 0)
50 | #define API_FLAGS_HEADER_SENT 1
51 | #define API_FLAGS_DONT_SET_HEADER_BEFORE_SENDING API_FLAGS_HEADER_SENT*2
52 | #define API_FLAGS_FORM_ONLY_ON_NULL API_FLAGS_DONT_SET_HEADER_BEFORE_SENDING*2
53 | #endif
54 |
--------------------------------------------------------------------------------