1
/************************************************************************************\
2
http.c - HTTP/1.1 feeder and consumer
4
(c) 2008 Copyright Hewlett-Packard Development Company, LP
6
Permission is hereby granted, free of charge, to any person obtaining a copy
7
of this software and associated documentation files (the "Software"), to deal
8
in the Software without restriction, including without limitation the rights
9
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10
of the Software, and to permit persons to whom the Software is furnished to do
11
so, subject to the following conditions:
13
The above copyright notice and this permission notice shall be included in all
14
copies or substantial portions of the Software.
16
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
In order to support state-less connections, each HTTP/1.1 connection or session
24
must start with http_open and end with http_close.
26
Author: Naga Samrat Chowdary, Narla
27
\************************************************************************************/
43
#define _STRINGIZE(x) #x
44
#define STRINGIZE(x) _STRINGIZE(x)
46
#define BUG(args...) syslog(LOG_ERR, __FILE__ " " STRINGIZE(__LINE__) ": " args)
49
#define DBG(args...) syslog(LOG_INFO, __FILE__ " " STRINGIZE(__LINE__) ": " args)
50
#define DBG_DUMP(data, size) sysdump((data), (size))
51
#define DBG_SZ(args...) syslog(LOG_INFO, args)
54
#define DBG_DUMP(data, size)
55
#define DBG_SZ(args...)
58
#define EXCEPTION_TIMEOUT 45 /* seconds */
75
enum HTTP_STATE state;
77
int footer; /* current footer */
79
HPMUD_DEVICE dd; /* hpiod device descriptor */
80
HPMUD_CHANNEL cd; /* hpiod soap channel descriptor */
81
struct stream_buffer s;
85
static char *strnstr(const char *haystack, const char *needle, size_t n)
87
int i, len=strlen(needle);
89
for (i=0; *haystack && i<n; ++haystack, i++)
91
if (strncmp(haystack, needle, len) == 0)
93
return ((char *)haystack);
101
static void sysdump(const void *data, int size)
103
/* Dump size bytes of *data. Output looks like:
104
* [0000] 75 6E 6B 6E 6F 77 6E 20 30 FF 00 00 00 00 39 00 unknown 0.....9.
107
unsigned char *p = (unsigned char *)data;
110
char bytestr[4] = {0};
111
char addrstr[10] = {0};
112
char hexstr[16*3 + 5] = {0};
113
char charstr[16*1 + 5] = {0};
114
for(n=1;n<=size;n++) {
116
/* store address for this line */
117
snprintf(addrstr, sizeof(addrstr), "%.4d", (int)((p-(unsigned char *)data) & 0xffff));
121
if (isprint(c) == 0) {
125
/* store hex str (for left side) */
126
snprintf(bytestr, sizeof(bytestr), "%02X ", *p);
127
strncat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1);
129
/* store char str (for right side) */
130
snprintf(bytestr, sizeof(bytestr), "%c", c);
131
strncat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1);
135
DBG_SZ("[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr);
142
if (strlen(hexstr) > 0) {
143
/* print rest of buffer if not empty */
144
DBG_SZ("[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr);
149
/* Read data into stream buffer. Return specified "size" or less. Unused data is left in the stream. */
150
static int read_stream(struct http_session *ps, char *data, int size, int sec_timeout, int *bytes_read)
153
int tmo=sec_timeout; /* initial timeout */
154
int max=sizeof(ps->s.buf);
155
enum HPMUD_RESULT ret;
157
DBG("read_stream() ps=%p data=%p size=%d timeout=%d s.index=%d s.cnt=%d\n", ps, data, size, sec_timeout, ps->s.index, ps->s.cnt);
161
/* Return any data in the stream first. */
164
if (ps->s.cnt > size)
166
/* Return part of stream buffer. */
168
memcpy(data, &ps->s.buf[ps->s.index], len);
174
/* Return all of rbuf. */
176
memcpy(data, &ps->s.buf[ps->s.index], len);
177
ps->s.index = ps->s.cnt = 0; /* stream is empty reset */
180
DBG("-read_stream() bytes_read=%d s.index=%d s.cnt=%d\n", len, ps->s.index, ps->s.cnt);
184
/* Stream is empty read more data from device. */
185
ret = hpmud_read_channel(ps->dd, ps->cd, &ps->s.buf[ps->s.index], max-(ps->s.index + ps->s.cnt), tmo, &len);
186
if (ret == HPMUD_R_IO_TIMEOUT)
188
BUG("timeout reading data sec_timeout=%d\n", tmo);
191
if (ret != HPMUD_R_OK)
193
BUG("read_stream error stat=%d\n", ret);
198
BUG("read_stream error len=0\n"); /* shouldn't happen, but it does with jetdirect */
202
DBG("read_channel len=%d\n", len);
205
if (ps->s.cnt > size)
207
/* Return part of stream buffer. */
209
memcpy(data, &ps->s.buf[ps->s.index], len);
215
/* Return all of rbuf. */
217
memcpy(data, &ps->s.buf[ps->s.index], len);
218
ps->s.index = ps->s.cnt = 0; /* stream is empty reset */
223
DBG("-read_stream() bytes_read=%d s.index=%d s.cnt=%d\n", len, ps->s.index, ps->s.cnt);
229
static int read_char(struct http_session *ps, int sec_timeout)
233
if (read_stream(ps, (char *)&ch, 1, sec_timeout, &len))
239
/* Read a line of data. Line length is not known. */
240
static int read_line(struct http_session *ps, char *line, int line_size, int sec_timeout, int *bytes_read)
244
int tmo=sec_timeout; /* initial timeout */
248
while (total < (line_size-1))
250
ch = read_char(ps, tmo);
255
else if (ch == '\n' && cr)
256
break; /* done, found CRLF */
257
else if (ch == '\n' && lf)
258
break; /* done, found LFLF (for kiwi "501 Not Implemented") */
262
goto bugout; /* error */
268
tmo=3; /* changed 1 to 3 for 1200dpi uncompressed, DES 8/20/08. */
274
*bytes_read=total; /* length does not include null termination */
275
DBG("read_line len=%d index=%d cnt=%d\n", total, ps->s.index, ps->s.cnt);
279
/* Http_open must be called for each HTTP/1.1 connection or session. */
280
enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_open(HPMUD_DEVICE dd, const char *channel, HTTP_HANDLE *handle)
282
struct http_session *ps;
283
enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
285
DBG("http_open() dd=%d channel=%s handle=%p\n", dd, channel, handle);
289
if ((ps = malloc(sizeof(struct http_session))) == NULL)
291
BUG("malloc failed: %m\n");
292
return HTTP_R_MALLOC_ERROR;
294
memset(ps, 0, sizeof(struct http_session));
297
if (hpmud_open_channel(ps->dd, channel, &ps->cd) != HPMUD_R_OK)
299
BUG("unable to open %s channel\n", channel);
303
ps->state = HS_ACTIVE;
308
if (stat != HTTP_R_OK)
313
/* Http_close must be called after the HTTP/1.1 connection closes. */
314
enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_close(HTTP_HANDLE handle)
316
struct http_session *ps = (struct http_session *)handle;
317
DBG("http_close() handle=%p\n", handle);
319
hpmud_close_channel(ps->dd, ps->cd);
324
/* Read HTTP/1.1 header. Blocks until header is read or timeout. */
325
enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read_header(HTTP_HANDLE handle, void *data, int max_size, int sec_timeout, int *bytes_read)
327
struct http_session *ps = (struct http_session *)handle;
329
int tmo=sec_timeout; /* set initial timeout */
330
enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
332
DBG("http_read_header() handle=%p data=%p size=%d sectime=%d\n", handle, data, max_size, sec_timeout);
336
/* Read initial HTTP/1.1 header status line. */
337
if (read_line(ps, data, max_size, tmo, &len))
339
ps->http_status = strtol(data+9, NULL, 10);
341
/* Check for good status, ignore 400 (no job id found for JobCancelRequest) */
342
if (!((ps->http_status >= 200 && ps->http_status < 300) || ps->http_status == 400))
344
BUG("invalid http_status=%d\n", ps->http_status);
346
/* Dump any outstanding payload here. */
347
while (!read_stream(ps, data, max_size, 1, &len))
348
BUG("dumping len=%d\n", len);
352
/* Read rest of header. Look for blank line. */
356
if (read_line(ps, data+total, max_size-total, tmo, &len))
363
DBG("-http_read_header() handle=%p data=%p bytes_read=%d size=%d status=%d\n", handle, data, *bytes_read, max_size, stat);
369
/* Reads data from HTTP/1.1 chunked data stream until EOF. Returns max_size or less. */
370
enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read_payload(HTTP_HANDLE handle, void *data, int max_size, int sec_timeout, int *bytes_read)
372
struct http_session *ps = (struct http_session *)handle;
375
int tmo=sec_timeout; /* set initial timeout */
376
enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
378
DBG("http_read_payload() handle=%p data=%p size=%d sectime=%d\n", handle, data, max_size, sec_timeout);
382
if (ps->state == HS_EOF)
390
/* Footer is not complete. Continue reading payload. */
391
if (read_stream(ps, data, ps->footer < max_size ? ps->footer : max_size, tmo, &len))
399
if (read_line(ps, line, sizeof(line), tmo, &len)) /* footer is complete, eat CRLF */
406
/* Read new footer. */
407
if (read_line(ps, line, sizeof(line), tmo, &len))
409
ps->footer = strtol(line, NULL, 16);
411
/* Check for zero footer. */
414
/* Done eat blank line. */
415
read_line(ps, line, sizeof(line), 1, &len);
421
/* Got a valid footer, continue reading payload. */
422
if (read_stream(ps, data, ps->footer < max_size ? ps->footer : max_size, tmo, &len))
430
if (read_line(ps, line, sizeof(line), tmo, &len)) /* footer is complete, eat CRLF */
436
} /* if (ps->state == HS_EOF) */
438
DBG("-http_read_payload() handle=%p data=%p bytes_read=%d size=%d status=%d\n", handle, data, *bytes_read, max_size, stat);
444
/* Reads data from HTTP/1.1 chunked data stream until EOF. Returns max_size or less. */
445
enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read(HTTP_HANDLE handle, void *data, int max_size, int sec_timeout, int *bytes_read)
447
struct http_session *ps = (struct http_session *)handle;
450
int tmo=sec_timeout; /* set initial timeout */
451
enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
452
int total_payload_length=*bytes_read;
454
DBG("http_read() handle=%p data=%p size=%d sectime=%d\n", handle, data, max_size, sec_timeout);
456
ps->footer=total_payload_length;
460
/* Read new footer. */
463
if (read_line(ps, line, sizeof(line), tmo, &len)){
464
*bytes_read = (ps->footer) * (-1) + 12;
473
if(ps->footer == 0) stat=HTTP_R_EOF;
475
DBG("-http_read() handle=%p data=%p bytes_read=%d size=%d status=%d\n", handle, data, *bytes_read, max_size, stat);
481
enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read_size(HTTP_HANDLE handle, void *data, int max_size, int sec_timeout, int *bytes_read)
483
struct http_session *ps = (struct http_session *)handle;
484
enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
486
if(ps && ps->state == HTTP_R_EOF) return HTTP_R_EOF;
489
ps->state = HTTP_R_EOF;
493
DBG("http_read_size() handle=%p data=%p size=%d sectime=%d\n", handle, data, max_size, sec_timeout);
496
while(*bytes_read < max_size)
498
*((char*)data + (*bytes_read)) = read_char(ps, sec_timeout);
499
*bytes_read = *bytes_read+1;
502
return stat = HTTP_R_OK;
505
/* Write data to HTTP/1.1 connection. Blocks until all data is written or timeout. Caller formats header, footer and payload. */
506
enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_write(HTTP_HANDLE handle, void *data, int size, int sec_timeout)
508
struct http_session *ps = (struct http_session *)handle;
510
int tmo=sec_timeout; /* set initial timeout */
511
enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
513
DBG("http_write() handle=%p data=%p size=%d sectime=%d\n", handle, data, size, sec_timeout);
515
if (hpmud_write_channel(ps->dd, ps->cd, data, size, tmo, &len) != HPMUD_R_OK)
517
BUG("unable to write channel data\n");