~ubuntu-branches/ubuntu/wily/pianobar/wily-proposed

« back to all changes in this revision

Viewing changes to src/libwaitress/waitress.c

  • Committer: Bazaar Package Importer
  • Author(s): Luke Faraone
  • Date: 2011-02-08 17:23:25 UTC
  • mfrom: (1.3.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20110208172325-0qf3sxpsu37j5ez9
Tags: 2011.01.24-1
* New upstream version. 
* Switch to DEP5 copyright.
* Augment CFLAGS to use the c99 standard.
* Don't install the now-removed AUTHORS file into docs.
* Drop dep on cmake.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Copyright (c) 2009-2010
 
3
        Lars-Dominik Braun <lars@6xq.net>
 
4
 
 
5
Permission is hereby granted, free of charge, to any person obtaining a copy
 
6
of this software and associated documentation files (the "Software"), to deal
 
7
in the Software without restriction, including without limitation the rights
 
8
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
9
copies of the Software, and to permit persons to whom the Software is
 
10
furnished to do so, subject to the following conditions:
 
11
 
 
12
The above copyright notice and this permission notice shall be included in
 
13
all copies or substantial portions of the Software.
 
14
 
 
15
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
16
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
17
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
18
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
19
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
20
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
21
THE SOFTWARE.
 
22
*/
 
23
 
 
24
#define _POSIX_C_SOURCE 1 /* required by getaddrinfo() */
 
25
#define _BSD_SOURCE /* snprintf() */
 
26
 
 
27
#include <sys/types.h>
 
28
#include <sys/socket.h>
 
29
#include <netdb.h>
 
30
#include <string.h>
 
31
#include <unistd.h>
 
32
#include <stdio.h>
 
33
#include <stdlib.h>
 
34
#include <ctype.h>
 
35
#include <fcntl.h>
 
36
#include <poll.h>
 
37
#include <errno.h>
 
38
 
 
39
#include "config.h"
 
40
#include "waitress.h"
 
41
 
 
42
typedef struct {
 
43
        char *buf;
 
44
        size_t pos;
 
45
} WaitressFetchBufCbBuffer_t;
 
46
 
 
47
inline void WaitressInit (WaitressHandle_t *waith) {
 
48
        memset (waith, 0, sizeof (*waith));
 
49
        waith->socktimeout = 30000;
 
50
}
 
51
 
 
52
inline void WaitressFree (WaitressHandle_t *waith) {
 
53
        memset (waith, 0, sizeof (*waith));
 
54
}
 
55
 
 
56
/*      Proxy set up?
 
57
 *      @param Waitress handle
 
58
 *      @return true|false
 
59
 */
 
60
static inline char WaitressProxyEnabled (const WaitressHandle_t *waith) {
 
61
        return *waith->proxyHost != '\0' && *waith->proxyPort != '\0';
 
62
}
 
63
 
 
64
/*      Set http proxy
 
65
 *      @param waitress handle
 
66
 *      @param host
 
67
 *      @param port
 
68
 */
 
69
inline void WaitressSetProxy (WaitressHandle_t *waith, const char *host,
 
70
                const char *port) {
 
71
        strncpy (waith->proxyHost, host, sizeof (waith->proxyHost)-1);
 
72
        strncpy (waith->proxyPort, port, sizeof (waith->proxyPort)-1);
 
73
}
 
74
 
 
75
/*      urlencode post-data
 
76
 *      @param encode this
 
77
 *      @return malloc'ed encoded string, don't forget to free it
 
78
 */
 
79
char *WaitressUrlEncode (const char *in) {
 
80
        size_t inLen = strlen (in);
 
81
        /* worst case: encode all characters */
 
82
        char *out = calloc (inLen * 3 + 1, sizeof (*in));
 
83
        const char *inPos = in;
 
84
        char *outPos = out;
 
85
 
 
86
        while (inPos - in < inLen) {
 
87
                if (!isalnum (*inPos) && *inPos != '_' && *inPos != '-' && *inPos != '.') {
 
88
                        *outPos++ = '%';
 
89
                        snprintf (outPos, 3, "%02x", *inPos & 0xff);
 
90
                        outPos += 2;
 
91
                } else {
 
92
                        /* copy character */
 
93
                        *outPos++ = *inPos;
 
94
                }
 
95
                ++inPos;
 
96
        }
 
97
 
 
98
        return out;
 
99
}
 
100
 
 
101
/*      Split http url into host, port and path
 
102
 *      @param url
 
103
 *      @param return buffer: host
 
104
 *      @param host buffer size
 
105
 *      @param return buffer: port, defaults to 80
 
106
 *      @param port buffer size
 
107
 *      @param return buffer: path
 
108
 *      @param path buffer size
 
109
 *      @param 1 = ok, 0 = not a http url; if your buffers are too small horrible
 
110
 *                      things will happen... But 1 is returned anyway.
 
111
 */
 
112
char WaitressSplitUrl (const char *url, char *retHost, size_t retHostSize,
 
113
                char *retPort, size_t retPortSize, char *retPath, size_t retPathSize) {
 
114
        size_t urlSize = strlen (url);
 
115
        const char *urlPos = url, *lastPos;
 
116
        
 
117
        if (urlSize > sizeof ("http://")-1 &&
 
118
                        memcmp (url, "http://", sizeof ("http://")-1) == 0) {
 
119
                memset (retHost, 0, retHostSize);
 
120
                memset (retPort, 0, retPortSize);
 
121
                strncpy (retPort, "80", retPortSize-1);
 
122
                memset (retPath, 0, retPathSize);
 
123
 
 
124
                urlPos += sizeof ("http://")-1;
 
125
                lastPos = urlPos;
 
126
 
 
127
                /* find host */
 
128
                while (*urlPos != '\0' && *urlPos != ':' && *urlPos != '/' &&
 
129
                                urlPos - lastPos < retHostSize-1) {
 
130
                        *retHost++ = *urlPos++;
 
131
                }
 
132
                lastPos = urlPos;
 
133
 
 
134
                /* port, if available */
 
135
                if (*urlPos == ':') {
 
136
                        /* skip : */
 
137
                        ++urlPos;
 
138
                        ++lastPos;
 
139
                        while (*urlPos != '\0' && *urlPos != '/' &&
 
140
                                        urlPos - lastPos < retPortSize-1) {
 
141
                                *retPort++ = *urlPos++;
 
142
                        }
 
143
                }
 
144
                lastPos = urlPos;
 
145
 
 
146
                /* path */
 
147
                while (*urlPos != '\0' && *urlPos != '#' &&
 
148
                                urlPos - lastPos < retPathSize-1) {
 
149
                        *retPath++ = *urlPos++;
 
150
                }
 
151
        } else {
 
152
                return 0;
 
153
        }
 
154
        return 1;
 
155
}
 
156
 
 
157
/*      Parse url and set host, port, path
 
158
 *      @param Waitress handle
 
159
 *      @param url: protocol://host:port/path
 
160
 */
 
161
inline char WaitressSetUrl (WaitressHandle_t *waith, const char *url) {
 
162
        return WaitressSplitUrl (url, waith->host, sizeof (waith->host),
 
163
                waith->port, sizeof (waith->port), waith->path, sizeof (waith->path));
 
164
}
 
165
 
 
166
/*      Set host, port, path
 
167
 *      @param Waitress handle
 
168
 *      @param host
 
169
 *      @param port (getaddrinfo () needs a string...)
 
170
 *      @param path, including leading /
 
171
 */
 
172
inline void WaitressSetHPP (WaitressHandle_t *waith, const char *host,
 
173
                const char *port, const char *path) {
 
174
        strncpy (waith->host, host, sizeof (waith->host)-1);
 
175
        strncpy (waith->port, port, sizeof (waith->port)-1);
 
176
        strncpy (waith->path, path, sizeof (waith->path)-1);
 
177
}
 
178
 
 
179
/*      Callback for WaitressFetchBuf, appends received data to NULL-terminated buffer
 
180
 *      @param received data
 
181
 *      @param data size
 
182
 *      @param buffer structure
 
183
 */
 
184
static WaitressCbReturn_t WaitressFetchBufCb (void *recvData, size_t recvDataSize,
 
185
                void *extraData) {
 
186
        char *recvBytes = recvData;
 
187
        WaitressFetchBufCbBuffer_t *buffer = extraData;
 
188
 
 
189
        if (buffer->buf == NULL) {
 
190
                if ((buffer->buf = malloc (sizeof (*buffer->buf) *
 
191
                                (recvDataSize + 1))) == NULL) {
 
192
                        return WAITRESS_CB_RET_ERR;
 
193
                }
 
194
        } else {
 
195
                char *newbuf;
 
196
                if ((newbuf = realloc (buffer->buf,
 
197
                                sizeof (*buffer->buf) *
 
198
                                (buffer->pos + recvDataSize + 1))) == NULL) {
 
199
                        free (buffer->buf);
 
200
                        return WAITRESS_CB_RET_ERR;
 
201
                }
 
202
                buffer->buf = newbuf;
 
203
        }
 
204
        memcpy (buffer->buf + buffer->pos, recvBytes, recvDataSize);
 
205
        buffer->pos += recvDataSize;
 
206
        *(buffer->buf+buffer->pos) = '\0';
 
207
 
 
208
        return WAITRESS_CB_RET_OK;
 
209
}
 
210
 
 
211
/*      Fetch string. Beware! This overwrites your waith->data pointer
 
212
 *      @param waitress handle
 
213
 *      @param result buffer, malloced (don't forget to free it yourself)
 
214
 */
 
215
WaitressReturn_t WaitressFetchBuf (WaitressHandle_t *waith, char **buf) {
 
216
        WaitressFetchBufCbBuffer_t buffer;
 
217
        WaitressReturn_t wRet;
 
218
 
 
219
        memset (&buffer, 0, sizeof (buffer));
 
220
 
 
221
        waith->data = &buffer;
 
222
        waith->callback = WaitressFetchBufCb;
 
223
 
 
224
        wRet = WaitressFetchCall (waith);
 
225
        *buf = buffer.buf;
 
226
        return wRet;
 
227
}
 
228
 
 
229
/*      poll wrapper that retries after signal interrupts, required for socksify
 
230
 *      wrapper
 
231
 */
 
232
static int WaitressPollLoop (struct pollfd *fds, nfds_t nfds, int timeout) {
 
233
        int pollres = -1;
 
234
        int pollerr = 0;
 
235
 
 
236
        do {
 
237
                pollres = poll (fds, nfds, timeout);
 
238
                pollerr = errno;
 
239
                errno = 0;
 
240
        } while (pollerr == EINTR || pollerr == EINPROGRESS || pollerr == EAGAIN);
 
241
 
 
242
        return pollres;
 
243
}
 
244
 
 
245
/*      write () wrapper with poll () timeout
 
246
 *      @param socket fd
 
247
 *      @param write buffer
 
248
 *      @param write count bytes
 
249
 *      @param reuse existing pollfd structure
 
250
 *      @param timeout (microseconds)
 
251
 *      @return WAITRESS_RET_OK, WAITRESS_RET_TIMEOUT or WAITRESS_RET_ERR
 
252
 */
 
253
static WaitressReturn_t WaitressPollWrite (int sockfd, const char *buf, size_t count,
 
254
                struct pollfd *sockpoll, int timeout) {
 
255
        int pollres = -1;
 
256
 
 
257
        sockpoll->events = POLLOUT;
 
258
        pollres = WaitressPollLoop (sockpoll, 1, timeout);
 
259
        if (pollres == 0) {
 
260
                return WAITRESS_RET_TIMEOUT;
 
261
        } else if (pollres == -1) {
 
262
                return WAITRESS_RET_ERR;
 
263
        }
 
264
        if (write (sockfd, buf, count) == -1) {
 
265
                return WAITRESS_RET_ERR;
 
266
        }
 
267
        return WAITRESS_RET_OK;
 
268
}
 
269
 
 
270
/*      read () wrapper with poll () timeout
 
271
 *      @param socket fd
 
272
 *      @param write to this buf, not NULL terminated
 
273
 *      @param buffer size
 
274
 *      @param reuse existing pollfd struct
 
275
 *      @param timeout (in microseconds)
 
276
 *      @param read () return value/written bytes
 
277
 *      @return WAITRESS_RET_OK, WAITRESS_RET_TIMEOUT, WAITRESS_RET_ERR
 
278
 */
 
279
static WaitressReturn_t WaitressPollRead (int sockfd, char *buf, size_t count,
 
280
                struct pollfd *sockpoll, int timeout, ssize_t *retSize) {
 
281
        int pollres = -1;
 
282
 
 
283
        sockpoll->events = POLLIN;
 
284
        pollres = WaitressPollLoop (sockpoll, 1, timeout);
 
285
        if (pollres == 0) {
 
286
                return WAITRESS_RET_TIMEOUT;
 
287
        } else if (pollres == -1) {
 
288
                return WAITRESS_RET_ERR;
 
289
        }
 
290
        if ((*retSize = read (sockfd, buf, count)) == -1) {
 
291
                return WAITRESS_RET_READ_ERR;
 
292
        }
 
293
        return WAITRESS_RET_OK;
 
294
}
 
295
 
 
296
/* FIXME: compiler macros are ugly... */
 
297
#define CLOSE_RET(ret) close (sockfd); return ret;
 
298
#define WRITE_RET(buf, count) \
 
299
                if ((wRet = WaitressPollWrite (sockfd, buf, count, \
 
300
                                &sockpoll, waith->socktimeout)) != WAITRESS_RET_OK) { \
 
301
                        CLOSE_RET (wRet); \
 
302
                }
 
303
#define READ_RET(buf, count, size) \
 
304
                if ((wRet = WaitressPollRead (sockfd, buf, count, \
 
305
                                &sockpoll, waith->socktimeout, size)) != WAITRESS_RET_OK) { \
 
306
                        CLOSE_RET (wRet); \
 
307
                }
 
308
 
 
309
/*      Receive data from host and call *callback ()
 
310
 *      @param waitress handle
 
311
 *      @return WaitressReturn_t
 
312
 */
 
313
WaitressReturn_t WaitressFetchCall (WaitressHandle_t *waith) {
 
314
        struct addrinfo hints, *res;
 
315
        int sockfd;
 
316
        char recvBuf[WAITRESS_RECV_BUFFER];
 
317
        char writeBuf[2*1024];
 
318
        ssize_t recvSize = 0;
 
319
        WaitressReturn_t wRet = WAITRESS_RET_OK;
 
320
        struct pollfd sockpoll;
 
321
        int pollres;
 
322
        /* header parser vars */
 
323
        char *nextLine = NULL, *thisLine = NULL;
 
324
        enum {HDRM_HEAD, HDRM_LINES, HDRM_FINISHED} hdrParseMode = HDRM_HEAD;
 
325
        char statusCode[3], val[256];
 
326
        unsigned int bufFilled = 0;
 
327
 
 
328
        /* initialize */
 
329
        waith->contentLength = 0;
 
330
        waith->contentReceived = 0;
 
331
        memset (&hints, 0, sizeof hints);
 
332
 
 
333
        hints.ai_family = AF_UNSPEC;
 
334
        hints.ai_socktype = SOCK_STREAM;
 
335
 
 
336
        /* Use proxy? */
 
337
        if (WaitressProxyEnabled (waith)) {
 
338
                if (getaddrinfo (waith->proxyHost, waith->proxyPort, &hints, &res) != 0) {
 
339
                        return WAITRESS_RET_GETADDR_ERR;
 
340
                }
 
341
        } else {
 
342
                if (getaddrinfo (waith->host, waith->port, &hints, &res) != 0) {
 
343
                        return WAITRESS_RET_GETADDR_ERR;
 
344
                }
 
345
        }
 
346
 
 
347
        if ((sockfd = socket (res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) {
 
348
                freeaddrinfo (res);
 
349
                return WAITRESS_RET_SOCK_ERR;
 
350
        }
 
351
        sockpoll.fd = sockfd;
 
352
 
 
353
        /* we need shorter timeouts for connect() */
 
354
        fcntl (sockfd, F_SETFL, O_NONBLOCK);
 
355
 
 
356
        /* increase socket receive buffer */
 
357
        const int sockopt = 256*1024;
 
358
        setsockopt (sockfd, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof (sockopt));
 
359
 
 
360
        /* non-blocking connect will return immediately */
 
361
        connect (sockfd, res->ai_addr, res->ai_addrlen);
 
362
 
 
363
        sockpoll.events = POLLOUT;
 
364
        pollres = WaitressPollLoop (&sockpoll, 1, waith->socktimeout);
 
365
        freeaddrinfo (res);
 
366
        if (pollres == 0) {
 
367
                return WAITRESS_RET_TIMEOUT;
 
368
        } else if (pollres == -1) {
 
369
                return WAITRESS_RET_ERR;
 
370
        }
 
371
        /* check connect () return value */
 
372
        socklen_t pollresSize = sizeof (pollres);
 
373
        getsockopt (sockfd, SOL_SOCKET, SO_ERROR, &pollres, &pollresSize);
 
374
        if (pollres != 0) {
 
375
                return WAITRESS_RET_CONNECT_REFUSED;
 
376
        }
 
377
 
 
378
        /* send request */
 
379
        if (WaitressProxyEnabled (waith)) {
 
380
                snprintf (writeBuf, sizeof (writeBuf),
 
381
                        "%s http://%s:%s%s HTTP/1.0\r\n",
 
382
                        (waith->method == WAITRESS_METHOD_GET ? "GET" : "POST"),
 
383
                        waith->host, waith->port, waith->path);
 
384
        } else {
 
385
                snprintf (writeBuf, sizeof (writeBuf),
 
386
                        "%s %s HTTP/1.0\r\n",
 
387
                        (waith->method == WAITRESS_METHOD_GET ? "GET" : "POST"),
 
388
                        waith->path);
 
389
        }
 
390
        WRITE_RET (writeBuf, strlen (writeBuf));
 
391
 
 
392
        snprintf (writeBuf, sizeof (writeBuf),
 
393
                        "Host: %s\r\nUser-Agent: " PACKAGE "\r\n", waith->host);
 
394
        WRITE_RET (writeBuf, strlen (writeBuf));
 
395
 
 
396
        if (waith->method == WAITRESS_METHOD_POST && waith->postData != NULL) {
 
397
                snprintf (writeBuf, sizeof (writeBuf), "Content-Length: %zu\r\n",
 
398
                                strlen (waith->postData));
 
399
                WRITE_RET (writeBuf, strlen (writeBuf));
 
400
        }
 
401
        
 
402
        if (waith->extraHeaders != NULL) {
 
403
                WRITE_RET (waith->extraHeaders, strlen (waith->extraHeaders));
 
404
        }
 
405
        
 
406
        WRITE_RET ("\r\n", 2);
 
407
 
 
408
        if (waith->method == WAITRESS_METHOD_POST && waith->postData != NULL) {
 
409
                WRITE_RET (waith->postData, strlen (waith->postData));
 
410
        }
 
411
 
 
412
        /* receive answer */
 
413
        nextLine = recvBuf;
 
414
        while (hdrParseMode != HDRM_FINISHED) {
 
415
                READ_RET (recvBuf+bufFilled, sizeof (recvBuf)-1 - bufFilled, &recvSize);
 
416
                if (recvSize == 0) {
 
417
                        /* connection closed too early */
 
418
                        CLOSE_RET (WAITRESS_RET_CONNECTION_CLOSED);
 
419
                }
 
420
                bufFilled += recvSize;
 
421
                memset (recvBuf+bufFilled, 0, sizeof (recvBuf) - bufFilled);
 
422
                thisLine = recvBuf;
 
423
 
 
424
                /* split */
 
425
                while ((nextLine = strchr (thisLine, '\n')) != NULL &&
 
426
                                hdrParseMode != HDRM_FINISHED) {
 
427
                        /* make lines parseable by string routines */
 
428
                        *nextLine = '\0';
 
429
                        if (*(nextLine-1) == '\r') {
 
430
                                *(nextLine-1) = '\0';
 
431
                        }
 
432
                        /* skip \0 */
 
433
                        ++nextLine;
 
434
 
 
435
                        switch (hdrParseMode) {
 
436
                                /* Status code */
 
437
                                case HDRM_HEAD:
 
438
                                        if (sscanf (thisLine, "HTTP/1.%*1[0-9] %3[0-9] ",
 
439
                                                        statusCode) == 1) {
 
440
                                                if (memcmp (statusCode, "200", 3) == 0 ||
 
441
                                                                memcmp (statusCode, "206", 3) == 0) {
 
442
                                                        /* everything's fine... */
 
443
                                                } else if (memcmp (statusCode, "403", 3) == 0) {
 
444
                                                        CLOSE_RET (WAITRESS_RET_FORBIDDEN);
 
445
                                                } else if (memcmp (statusCode, "404", 3) == 0) {
 
446
                                                        CLOSE_RET (WAITRESS_RET_NOTFOUND);
 
447
                                                } else {
 
448
                                                        CLOSE_RET (WAITRESS_RET_STATUS_UNKNOWN);
 
449
                                                }
 
450
                                                hdrParseMode = HDRM_LINES;
 
451
                                        } /* endif */
 
452
                                        break;
 
453
 
 
454
                                /* Everything else, except status code */
 
455
                                case HDRM_LINES:
 
456
                                        /* empty line => content starts here */
 
457
                                        if (*thisLine == '\0') {
 
458
                                                hdrParseMode = HDRM_FINISHED;
 
459
                                        } else {
 
460
                                                memset (val, 0, sizeof (val));
 
461
                                                if (sscanf (thisLine, "Content-Length: %255c", val) == 1) {
 
462
                                                        waith->contentLength = atol (val);
 
463
                                                }
 
464
                                        }
 
465
                                        break;
 
466
 
 
467
                                default:
 
468
                                        break;
 
469
                        } /* end switch */
 
470
                        thisLine = nextLine;
 
471
                } /* end while strchr */
 
472
                memmove (recvBuf, thisLine, thisLine-recvBuf);
 
473
                bufFilled -= (thisLine-recvBuf);
 
474
        } /* end while hdrParseMode */
 
475
 
 
476
        /* push remaining bytes */
 
477
        if (bufFilled > 0) {
 
478
                waith->contentReceived += bufFilled;
 
479
                if (waith->callback (thisLine, bufFilled, waith->data) ==
 
480
                                WAITRESS_CB_RET_ERR) {
 
481
                        CLOSE_RET (WAITRESS_RET_CB_ABORT);
 
482
                }
 
483
        }
 
484
 
 
485
        /* receive content */
 
486
        do {
 
487
                READ_RET (recvBuf, sizeof (recvBuf), &recvSize);
 
488
                if (recvSize > 0) {
 
489
                        waith->contentReceived += recvSize;
 
490
                        if (waith->callback (recvBuf, recvSize, waith->data) ==
 
491
                                        WAITRESS_CB_RET_ERR) {
 
492
                                wRet = WAITRESS_RET_CB_ABORT;
 
493
                                break;
 
494
                        }
 
495
                }
 
496
        } while (recvSize > 0);
 
497
 
 
498
        close (sockfd);
 
499
 
 
500
        if (wRet == WAITRESS_RET_OK && waith->contentReceived < waith->contentLength) {
 
501
                return WAITRESS_RET_PARTIAL_FILE;
 
502
        }
 
503
        return wRet;
 
504
}
 
505
 
 
506
#undef CLOSE_RET
 
507
#undef WRITE_RET
 
508
#undef READ_RET
 
509
 
 
510
const char *WaitressErrorToStr (WaitressReturn_t wRet) {
 
511
        switch (wRet) {
 
512
                case WAITRESS_RET_OK:
 
513
                        return "Everything's fine :)";
 
514
                        break;
 
515
 
 
516
                case WAITRESS_RET_ERR:
 
517
                        return "Unknown.";
 
518
                        break;
 
519
 
 
520
                case WAITRESS_RET_STATUS_UNKNOWN:
 
521
                        return "Unknown HTTP status code.";
 
522
                        break;
 
523
 
 
524
                case WAITRESS_RET_NOTFOUND:
 
525
                        return "File not found.";
 
526
                        break;
 
527
                
 
528
                case WAITRESS_RET_FORBIDDEN:
 
529
                        return "Forbidden.";
 
530
                        break;
 
531
 
 
532
                case WAITRESS_RET_CONNECT_REFUSED:
 
533
                        return "Connection refused.";
 
534
                        break;
 
535
 
 
536
                case WAITRESS_RET_SOCK_ERR:
 
537
                        return "Socket error.";
 
538
                        break;
 
539
 
 
540
                case WAITRESS_RET_GETADDR_ERR:
 
541
                        return "getaddr failed.";
 
542
                        break;
 
543
 
 
544
                case WAITRESS_RET_CB_ABORT:
 
545
                        return "Callback aborted request.";
 
546
                        break;
 
547
 
 
548
                case WAITRESS_RET_HDR_OVERFLOW:
 
549
                        return "HTTP header overflow.";
 
550
                        break;
 
551
 
 
552
                case WAITRESS_RET_PARTIAL_FILE:
 
553
                        return "Partial file.";
 
554
                        break;
 
555
        
 
556
                case WAITRESS_RET_TIMEOUT:
 
557
                        return "Timeout.";
 
558
                        break;
 
559
 
 
560
                case WAITRESS_RET_READ_ERR:
 
561
                        return "Read error.";
 
562
                        break;
 
563
 
 
564
                case WAITRESS_RET_CONNECTION_CLOSED:
 
565
                        return "Connection closed by remote host.";
 
566
                        break;
 
567
 
 
568
                default:
 
569
                        return "No error message available.";
 
570
                        break;
 
571
        }
 
572
}
 
573