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 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 static char *strnstr(const char *haystack, const char *needle, size_t n)
88 int i, len=strlen(needle);
90 for (i=0; *haystack && i<n; ++haystack, i++)
92 if (strncmp(haystack, needle, len) == 0)
94 return ((char *)haystack);
102 static void sysdump(const void *data, int size)
104 /* Dump size bytes of *data. Output looks like:
105 * [0000] 75 6E 6B 6E 6F 77 6E 20 30 FF 00 00 00 00 39 00 unknown 0.....9.
108 unsigned char *p = (unsigned char *)data;
111 char bytestr[4] = {0};
112 char addrstr[10] = {0};
113 char hexstr[16*3 + 5] = {0};
114 char charstr[16*1 + 5] = {0};
115 for(n=1;n<=size;n++) {
117 /* store address for this line */
118 snprintf(addrstr, sizeof(addrstr), "%.4d", (int)((p-(unsigned char *)data) & 0xffff));
122 if (isprint(c) == 0) {
126 /* store hex str (for left side) */
127 snprintf(bytestr, sizeof(bytestr), "%02X ", *p);
128 strncat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1);
130 /* store char str (for right side) */
131 snprintf(bytestr, sizeof(bytestr), "%c", c);
132 strncat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1);
136 DBG_SZ("[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr);
143 if (strlen(hexstr) > 0) {
144 /* print rest of buffer if not empty */
145 DBG_SZ("[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr);
150 /* Read data into stream buffer. Return specified "size" or less. Unused data is left in the stream. */
151 static int read_stream(struct http_session *ps, char *data, int size, int sec_timeout, int *bytes_read)
154 int tmo=sec_timeout; /* initial timeout */
155 int max=sizeof(ps->s.buf);
156 enum HPMUD_RESULT ret;
158 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);
162 /* Return any data in the stream first. */
165 if (ps->s.cnt > size)
167 /* Return part of stream buffer. */
169 memcpy(data, &ps->s.buf[ps->s.index], len);
175 /* Return all of rbuf. */
177 memcpy(data, &ps->s.buf[ps->s.index], len);
178 ps->s.index = ps->s.cnt = 0; /* stream is empty reset */
181 DBG("-read_stream() bytes_read=%d s.index=%d s.cnt=%d\n", len, ps->s.index, ps->s.cnt);
185 /* Stream is empty read more data from device. */
186 ret = hpmud_read_channel(ps->dd, ps->cd, &ps->s.buf[ps->s.index], max-(ps->s.index + ps->s.cnt), tmo, &len);
187 if (ret == HPMUD_R_IO_TIMEOUT)
189 BUG("timeout reading data sec_timeout=%d\n", tmo);
192 if (ret != HPMUD_R_OK)
194 BUG("read_stream error stat=%d\n", ret);
199 BUG("read_stream error len=0\n"); /* shouldn't happen, but it does with jetdirect */
203 DBG("read_channel len=%d\n", len);
206 if (ps->s.cnt > size)
208 /* Return part of stream buffer. */
210 memcpy(data, &ps->s.buf[ps->s.index], len);
216 /* Return all of rbuf. */
218 memcpy(data, &ps->s.buf[ps->s.index], len);
219 ps->s.index = ps->s.cnt = 0; /* stream is empty reset */
224 DBG("-read_stream() bytes_read=%d s.index=%d s.cnt=%d\n", len, ps->s.index, ps->s.cnt);
230 static int read_char(struct http_session *ps, int sec_timeout)
234 if (read_stream(ps, (char *)&ch, 1, sec_timeout, &len))
240 /* Read a line of data. Line length is not known. */
241 static int read_line(struct http_session *ps, char *line, int line_size, int sec_timeout, int *bytes_read)
245 int tmo=sec_timeout; /* initial timeout */
249 while (total < (line_size-1))
251 ch = read_char(ps, tmo);
256 else if (ch == '\n' && cr)
257 break; /* done, found CRLF */
258 else if (ch == '\n' && lf)
259 break; /* done, found LFLF (for kiwi "501 Not Implemented") */
263 goto bugout; /* error */
269 tmo=3; /* changed 1 to 3 for 1200dpi uncompressed, DES 8/20/08. */
275 *bytes_read=total; /* length does not include null termination */
276 DBG("read_line len=%d index=%d cnt=%d\n", total, ps->s.index, ps->s.cnt);
280 /* Http_open must be called for each HTTP/1.1 connection or session. */
281 enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_open(HPMUD_DEVICE dd, const char *channel, HTTP_HANDLE *handle)
283 struct http_session *ps;
284 enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
286 DBG("http_open() dd=%d channel=%s handle=%p\n", dd, channel, handle);
290 if ((ps = malloc(sizeof(struct http_session))) == NULL)
292 BUG("malloc failed: %m\n");
293 return HTTP_R_MALLOC_ERROR;
295 memset(ps, 0, sizeof(struct http_session));
298 if (hpmud_open_channel(ps->dd, channel, &ps->cd) != HPMUD_R_OK)
300 BUG("unable to open %s channel\n", channel);
304 ps->state = HS_ACTIVE;
309 if (stat != HTTP_R_OK)
314 /* Http_close must be called after the HTTP/1.1 connection closes. */
315 enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_close(HTTP_HANDLE handle)
317 struct http_session *ps = (struct http_session *)handle;
318 DBG("http_close() handle=%p\n", handle);
320 hpmud_close_channel(ps->dd, ps->cd);
325 /* Read HTTP/1.1 header. Blocks until header is read or timeout. */
326 enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read_header(HTTP_HANDLE handle, void *data, int max_size, int sec_timeout, int *bytes_read)
328 struct http_session *ps = (struct http_session *)handle;
330 int tmo=sec_timeout; /* set initial timeout */
331 enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
333 DBG("http_read_header() handle=%p data=%p size=%d sectime=%d\n", handle, data, max_size, sec_timeout);
337 /* Read initial HTTP/1.1 header status line. */
338 if (read_line(ps, data, max_size, tmo, &len))
340 ps->http_status = strtol(data+9, NULL, 10);
341 *bytes_read = total = len;
343 /* Check for good status, ignore 400 (no job id found for JobCancelRequest) */
344 if (!((ps->http_status >= 200 && ps->http_status < 300) || ps->http_status == 400))
346 BUG("invalid http_status=%d\n", ps->http_status);
348 /* Dump any outstanding payload here. */
349 while (!read_stream(ps, data, max_size, 1, &len))
350 BUG("dumping len=%d\n", len);
354 /* Read rest of header. Look for blank line. */
355 *bytes_read = total = len;
358 if (read_line(ps, data+total, max_size-total, tmo, &len))
362 DBG("http_read_header data=%s len=%d total=%d\n", (char*)data+total, len, total);
366 DBG("-http_read_header() handle=%p data=%p bytes_read=%d size=%d status=%d\n", handle, data, *bytes_read, max_size, stat);
372 /* Reads data from HTTP/1.1 chunked data stream until EOF. Returns max_size or less. */
373 enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read_payload(HTTP_HANDLE handle, void *data, int max_size, int sec_timeout, int *bytes_read)
375 struct http_session *ps = (struct http_session *)handle;
378 int tmo=sec_timeout; /* set initial timeout */
379 enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
381 DBG("http_read_payload() handle=%p data=%p size=%d sectime=%d\n", handle, data, max_size, sec_timeout);
385 if (ps->state == HS_EOF)
393 /* Footer is not complete. Continue reading payload. */
394 if (read_stream(ps, data, ps->footer < max_size ? ps->footer : max_size, tmo, &len))
402 if (read_line(ps, line, sizeof(line), tmo, &len)) /* footer is complete, eat CRLF */
409 /* Read new footer. */
410 if (read_line(ps, line, sizeof(line), tmo, &len))
412 ps->footer = strtol(line, NULL, 16);
414 /* Check for zero footer. */
417 /* Done eat blank line. */
418 read_line(ps, line, sizeof(line), 1, &len);
424 /* Got a valid footer, continue reading payload. */
425 if (read_stream(ps, data, ps->footer < max_size ? ps->footer : max_size, tmo, &len))
433 if (read_line(ps, line, sizeof(line), tmo, &len)) /* footer is complete, eat CRLF */
439 } /* if (ps->state == HS_EOF) */
441 DBG("-http_read_payload() handle=%p data=%p bytes_read=%d size=%d status=%d\n", handle, data, *bytes_read, max_size, stat);
447 /* Reads data from HTTP/1.1 chunked data stream until EOF. Returns max_size or less. */
448 enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read(HTTP_HANDLE handle, void *data, int max_size, int sec_timeout, int *bytes_read)
450 struct http_session *ps = (struct http_session *)handle;
451 char line[128] ={0,};
453 int tmo=sec_timeout; /* set initial timeout */
454 enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
455 int total_payload_length=*bytes_read;
457 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);
459 ps->footer=total_payload_length;
463 /* Read new footer. */
464 if (ps->footer) //Payload length is known
468 if (read_line(ps, line, sizeof(line), tmo, &len))
470 *bytes_read = (ps->footer) * (-1) + 12;
483 ret = read_line (ps, line, sizeof(line), tmo, &len);
485 if(ret) //failed to read line
492 DBG("http_read len=%d datalen=%d data=%s\n", len, strlen((char*)data), (char*)data);
493 //Check for the footer
494 if (strncmp(data-7, ZERO_FOOTER, sizeof(ZERO_FOOTER)-1) == 0)
502 if(ps->footer == 0) stat=HTTP_R_EOF;
504 DBG("-http_read() handle=%p data=%p bytes_read=%d size=%d status=%d\n", handle, data, *bytes_read, max_size, stat);
510 enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read_size(HTTP_HANDLE handle, void *data, int max_size, int sec_timeout, int *bytes_read)
512 struct http_session *ps = (struct http_session *)handle;
513 enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
515 if(ps && ps->state == HTTP_R_EOF) return HTTP_R_EOF;
518 ps->state = HTTP_R_EOF;
522 DBG("http_read_size() handle=%p data=%p size=%d sectime=%d\n", handle, data, max_size, sec_timeout);
525 while(*bytes_read < max_size)
527 *((char*)data + (*bytes_read)) = read_char(ps, sec_timeout);
528 *bytes_read = *bytes_read+1;
531 return stat = HTTP_R_OK;
534 /* Write data to HTTP/1.1 connection. Blocks until all data is written or timeout. Caller formats header, footer and payload. */
535 enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_write(HTTP_HANDLE handle, void *data, int size, int sec_timeout)
537 struct http_session *ps = (struct http_session *)handle;
539 int tmo=sec_timeout; /* set initial timeout */
540 enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
542 DBG("http_write() handle=%p data=%p size=%d sectime=%d\n", handle, data, size, sec_timeout);
544 if (hpmud_write_channel(ps->dd, ps->cd, data, size, tmo, &len) != HPMUD_R_OK)
546 BUG("unable to write channel data\n");
556 void __attribute__ ((visibility ("hidden"))) http_unchunk_data(char *buffer)
562 //Here buffer starts like "<?xml....". There is no chunklen, only buffer
567 if (!(*p == '\n' || *p == '\r' || *p =='\t'))
577 /*Here buffer looks like "chunklen data chunklen data 0"
578 e.g "FE3 <?xml.... 8E8 ... 0"*/
581 while(*p != '\n' && *p != '\r')
583 chunklen = chunklen << 4 ; //Multiply 16
584 if ('0' <= *p && *p<='9')
585 chunklen += *p - '0';
586 else if ('A' <= *p && *p <= 'F')
587 chunklen += 10 - 'A' + *p;
588 else if ('a' <= *p && *p <= 'f')
589 chunklen += 10 + *p - 'a';
592 chunklen = chunklen >> 4;
599 while(*p == '\n' || *p == '\r' || *p =='\t') p++;
600 //copy the data till chunklen
603 if (!(*p == '\n' || *p == '\r' || *p =='\t'))
611 while(*p == '\n' || *p == '\r' || *p =='\t') p++;