1
/************************************************************************************\
2
http.c - HTTP/1.1 feeder and consumer
4
(c) 2008 Copyright HP 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
Contributing Author: Sarbeswar Meher
28
\************************************************************************************/
44
#define _STRINGIZE(x) #x
45
#define STRINGIZE(x) _STRINGIZE(x)
47
#define BUG(args...) syslog(LOG_ERR, __FILE__ " " STRINGIZE(__LINE__) ": " args)
50
#define DBG(args...) syslog(LOG_INFO, __FILE__ " " STRINGIZE(__LINE__) ": " args)
51
#define DBG_DUMP(data, size) sysdump((data), (size))
52
#define DBG_SZ(args...) syslog(LOG_INFO, args)
55
#define DBG_DUMP(data, size)
56
#define DBG_SZ(args...)
59
#define EXCEPTION_TIMEOUT 45 /* seconds */
76
enum HTTP_STATE state;
78
int footer; /* current footer */
80
HPMUD_DEVICE dd; /* hpiod device descriptor */
81
HPMUD_CHANNEL cd; /* hpiod soap channel descriptor */
82
struct stream_buffer s;
86
/* Copy stream data to data buffer and then clear stream. */
87
int __attribute__ ((visibility ("hidden"))) clear_stream(HTTP_HANDLE handle, void *data, int max_size, int *bytes_read)
90
DBG("clear_stream entry...\n");
91
struct http_session *ps = (struct http_session *)handle;
94
if (ps->s.cnt > 0 && ps->s.cnt <= max_size )
97
DBG("Clearing (%d) bytes from the stream\n", len);
98
memcpy(data, &ps->s.buf[ps->s.index], len);
99
ps->s.index = ps->s.cnt = 0; /* stream is empty reset */
105
DBG("clear_stream returning with (stat = %d, bytes_read = %d)...\n", stat, *bytes_read);
111
/* Read data into stream buffer. Return specified "size" or less. Unused data is left in the stream. */
112
static int read_stream(struct http_session *ps, char *data, int size, int sec_timeout, int *bytes_read)
115
int tmo=sec_timeout; /* initial timeout */
116
int max=sizeof(ps->s.buf);
117
enum HPMUD_RESULT ret;
120
//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);
124
/* Return any data in the stream first. */
127
if (ps->s.cnt > size)
129
/* Return part of stream buffer. */
131
memcpy(data, &ps->s.buf[ps->s.index], len);
137
/* Return all of rbuf. */
139
memcpy(data, &ps->s.buf[ps->s.index], len);
140
ps->s.index = ps->s.cnt = 0; /* stream is empty reset */
143
//DBG("-read_stream() bytes_read=%d s.index=%d s.cnt=%d\n", len, ps->s.index, ps->s.cnt);
147
/* Stream is empty read more data from device. */
148
ret = hpmud_read_channel(ps->dd, ps->cd, &ps->s.buf[ps->s.index], max-(ps->s.index + ps->s.cnt), tmo, &len);
149
while ( (ret == HPMUD_R_IO_TIMEOUT || ret == HPMUD_R_IO_ERROR) && retry--)
151
usleep(100000); //Pause for 0.1 sec. Sometimes devices like scanjet 3500 take some time to prepare data.
152
ret = hpmud_read_channel(ps->dd, ps->cd, &ps->s.buf[ps->s.index], max-(ps->s.index + ps->s.cnt), tmo, &len);
153
DBG("hpmud_read_channel failed retrying (%d) more times)\n", retry);
155
if (ret != HPMUD_R_OK)
157
BUG("read_stream error stat=%d\n", ret);
162
BUG("read_stream error len=0\n"); /* shouldn't happen, but it does with jetdirect */
166
DBG("read_channel len=%d\n", len);
169
if (ps->s.cnt > size)
171
/* Return part of stream buffer. */
173
memcpy(data, &ps->s.buf[ps->s.index], len);
179
/* Return all of rbuf. */
181
memcpy(data, &ps->s.buf[ps->s.index], len);
182
ps->s.index = ps->s.cnt = 0; /* stream is empty reset */
189
DBG("-read_stream() bytes_read=%d s.index=%d s.cnt=%d stat=%d\n", len, ps->s.index, ps->s.cnt, stat);
193
static int read_char(struct http_session *ps, int sec_timeout)
197
if (read_stream(ps, (char *)&ch, 1, sec_timeout, &len))
203
/* Read a line of data. Line length is not known. */
204
static int read_line(struct http_session *ps, char *line, int line_size, int sec_timeout, int *bytes_read)
208
int tmo=sec_timeout; /* initial timeout */
212
while (total < (line_size-1))
214
ch = read_char(ps, tmo);
219
else if (ch == '\n' && cr)
220
break; /* done, found CRLF */
221
else if (ch == '\n' && lf)
222
break; /* done, found LFLF (for kiwi "501 Not Implemented") */
226
goto bugout; /* error */
232
tmo=3; /* changed 1 to 3 for 1200dpi uncompressed, DES 8/20/08. */
238
*bytes_read=total; /* length does not include null termination */
239
DBG("read_line len=%d index=%d cnt=%d\n", total, ps->s.index, ps->s.cnt);
243
/* Http_open must be called for each HTTP/1.1 connection or session. */
244
enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_open(HPMUD_DEVICE dd, const char *channel, HTTP_HANDLE *handle)
246
struct http_session *ps;
247
enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
249
DBG("http_open() dd=%d channel=%s handle=%p\n", dd, channel, handle);
253
if ((ps = malloc(sizeof(struct http_session))) == NULL)
255
BUG("malloc failed: %m\n");
256
return HTTP_R_MALLOC_ERROR;
258
memset(ps, 0, sizeof(struct http_session));
261
if (hpmud_open_channel(ps->dd, channel, &ps->cd) != HPMUD_R_OK)
263
BUG("unable to open %s channel\n", channel);
267
ps->state = HS_ACTIVE;
272
if (stat != HTTP_R_OK)
277
/* Http_close must be called after the HTTP/1.1 connection closes. */
278
enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_close(HTTP_HANDLE handle)
280
struct http_session *ps = (struct http_session *)handle;
284
DBG("http_close() handle=%p\n", handle);
286
hpmud_close_channel(ps->dd, ps->cd);
292
/* Read HTTP/1.1 header. Blocks until header is read or timeout. */
293
enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read_header(HTTP_HANDLE handle, void *data, int max_size, int sec_timeout, int *bytes_read)
295
struct http_session *ps = (struct http_session *)handle;
297
int tmo=sec_timeout; /* set initial timeout */
298
enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
300
DBG("http_read_header() handle=%p data=%p size=%d sectime=%d\n", handle, data, max_size, sec_timeout);
304
/* Read initial HTTP/1.1 header status line. */
307
if (read_line(ps, data, max_size, tmo, &len))
309
if( !strncmp(data, "HTTP/1.1", 8))
313
DBG("HTTP Header not found. Searching header in next line (%d)\n", len);
316
ps->http_status = strtol(data+9, NULL, 10);
317
*bytes_read = total = len;
319
/* Check for good status, ignore 400 (no job id found for JobCancelRequest) */
320
if (!((ps->http_status >= 200 && ps->http_status < 300) || ps->http_status == 400))
322
BUG("invalid http_status=%d\n", ps->http_status);
324
/* Dump any outstanding payload here. */
325
while (!read_stream(ps, data + total, max_size, 1, &len))
327
total = (total+len) % max_size;
328
BUG("dumping len=%d\n", len);
333
/* Read rest of header. Look for blank line. */
334
*bytes_read = total = len;
337
if (read_line(ps, data+total, max_size-total, tmo, &len))
341
DBG("http_read_header data=%s len=%d total=%d\n", (char*)data+total, len, total);
345
DBG("-http_read_header() handle=%p data=%p bytes_read=%d size=%d status=%d\n", handle, data, *bytes_read, max_size, stat);
351
/* Reads data from HTTP/1.1 chunked data stream until EOF. Returns max_size or less. */
352
enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read_payload(HTTP_HANDLE handle, void *data, int max_size, int sec_timeout, int *bytes_read)
354
struct http_session *ps = (struct http_session *)handle;
357
int tmo=sec_timeout; /* set initial timeout */
358
enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
360
DBG("http_read_payload() handle=%p data=%p size=%d sectime=%d\n", handle, data, max_size, sec_timeout);
364
if (ps->state == HS_EOF)
372
/* Footer is not complete. Continue reading payload. */
373
if (read_stream(ps, data, ps->footer < max_size ? ps->footer : max_size, tmo, &len))
381
if (read_line(ps, line, sizeof(line), tmo, &len)) /* footer is complete, eat CRLF */
388
/* Read new footer. */
389
if (read_line(ps, line, sizeof(line), tmo, &len))
391
ps->footer = strtol(line, NULL, 16);
393
/* Check for zero footer. */
396
/* Done eat blank line. */
397
read_line(ps, line, sizeof(line), 1, &len);
403
/* Got a valid footer, continue reading payload. */
404
if (read_stream(ps, data, ps->footer < max_size ? ps->footer : max_size, tmo, &len))
412
if (read_line(ps, line, sizeof(line), tmo, &len)) /* footer is complete, eat CRLF */
418
} /* if (ps->state == HS_EOF) */
420
DBG("-http_read_payload() handle=%p data=%p bytes_read=%d size=%d status=%d\n", handle, data, *bytes_read, max_size, stat);
426
enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read2(HTTP_HANDLE handle, void *data, int max_size, int tmo, int *bytes_read)
428
struct http_session *ps = (struct http_session *)handle;
429
enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
435
DBG("http_read2 entry.\n" );
436
ret = hpmud_read_channel(ps->dd, ps->cd, (char *)data, max_size, tmo, bytes_read);
439
DBG("http_read2 successful. (%d bytes read). \n", *bytes_read);
443
DBG("http_read2 failed. Retrying (%d) more times before exiting.\n", retry);
446
DBG("http_read2 failed to read (bytes_read=%d)\n", *bytes_read);
449
/* Reads data from HTTP/1.1 chunked data stream until EOF. Returns max_size or less. */
450
enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read(HTTP_HANDLE handle, void *data, int max_size, int sec_timeout, int *bytes_read)
452
struct http_session *ps = (struct http_session *)handle;
453
char line[128] ={0,};
455
int tmo=sec_timeout; /* set initial timeout */
456
enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
457
int total_payload_length=*bytes_read;
459
DBG("http_read() handle=%p data=%p size=%d sectime=%d total_payload_length=%d\n", handle, data, max_size, sec_timeout, total_payload_length);
461
ps->footer=total_payload_length;
465
/* Read new footer. */
466
if (ps->footer) //Payload length is known
470
if (read_line(ps, line, sizeof(line), tmo, &len))
472
*bytes_read = (ps->footer) * (-1) + 12;
485
ret = read_line (ps, line, sizeof(line), tmo, &len);
487
if(ret) //failed to read line
494
DBG("http_read len=%d datalen=%d data=%s\n", len, strlen((char*)data), (char*)data);
495
//Check for the footer
496
if (strncmp(data-7, ZERO_FOOTER, sizeof(ZERO_FOOTER)-1) == 0)
504
if(ps->footer == 0) stat=HTTP_R_EOF;
506
DBG("-http_read() handle=%p data=%p bytes_read=%d size=%d status=%d\n", handle, data, *bytes_read, max_size, stat);
512
enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read_size(HTTP_HANDLE handle, void *data, int max_size, int sec_timeout, int *bytes_read)
514
struct http_session *ps = (struct http_session *)handle;
515
enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
518
if(ps && ps->state == HTTP_R_EOF) return HTTP_R_EOF;
521
ps->state = HTTP_R_EOF;
525
DBG("http_read_size() handle=%p data=%p size=%d sectime=%d\n", handle, data, max_size, sec_timeout);
528
while(*bytes_read < max_size)
530
ch = read_char(ps, sec_timeout);
533
DBG("http_read_size(): IO error after %d bytes.\n",*bytes_read);
534
return HTTP_R_IO_ERROR;
537
*((char*)data + (*bytes_read)) = ch;
538
*bytes_read = *bytes_read+1;
541
return stat = HTTP_R_OK;
544
/* Write data to HTTP/1.1 connection. Blocks until all data is written or timeout. Caller formats header, footer and payload. */
545
enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_write(HTTP_HANDLE handle, void *data, int size, int sec_timeout)
547
struct http_session *ps = (struct http_session *)handle;
549
int tmo=sec_timeout; /* set initial timeout */
550
enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
552
DBG("http_write() handle=%p data=%p size=%d sectime=%d\n", handle, data, size, sec_timeout);
554
if (hpmud_write_channel(ps->dd, ps->cd, data, size, tmo, &len) != HPMUD_R_OK)
556
BUG("unable to write channel data\n");
566
void __attribute__ ((visibility ("hidden"))) http_unchunk_data(char *buffer)
572
//Here buffer starts like "<?xml....". There is no chunklen, only buffer
577
if (!(*p == '\n' || *p == '\r' || *p =='\t'))
587
/*Here buffer looks like "chunklen data chunklen data 0"
588
e.g "FE3 <?xml.... 8E8 ... 0"*/
591
while(*p != '\n' && *p != '\r')
593
chunklen = chunklen << 4 ; //Multiply 16
594
if ('0' <= *p && *p<='9')
595
chunklen += *p - '0';
596
else if ('A' <= *p && *p <= 'F')
597
chunklen += 10 - 'A' + *p;
598
else if ('a' <= *p && *p <= 'f')
599
chunklen += 10 + *p - 'a';
602
chunklen = chunklen >> 4;
609
while(*p == '\n' || *p == '\r' || *p =='\t') p++;
610
//copy the data till chunklen
613
if (!(*p == '\n' || *p == '\r' || *p =='\t'))
621
while(*p == '\n' || *p == '\r' || *p =='\t') p++;