HV *parseHeaders (char **data) { HV *hdrs = newHV(); char copy[8192], *p, *val = NULL, *str = *data; int state = NAME; SV **cur; while (1) { // extract field name if (state == NAME) { if (sscanf(str, "%[^:]:", copy) != 1) break; // malformed lowerCase(copy); cur = hv_fetch(hdrs, copy, strlen(copy), 1); if (!SvOK(*cur)) *cur = newSVpv("", 0); state = VALUE; continue; } // extract field value if (!val) { p = val = strchr(str, ':'); val++; while (*val == ' ' || *val == '\t') val++; } if (!(*p)) break; // malformed if (p[0] == '\r' && p[1] == '\n') { p += 2; if (*p > 32 || (p[0] == '\r' && p[1] == '\n')) { // next line starts with a new name or \r\n, so field is complete *(p-2) = '\0'; if (SvCUR(*cur)) { // multiple entries val--; *val = ','; } sv_catpv(*cur, val); if (p[0] == '\r' && p[1] == '\n') break; // good val = NULL; state = NAME; str = p; } } else p++; } *data = p; return hdrs; } HV *parseRequest (char *raw, int stop) { int len, state = METHOD; char *p = raw, *val, *qs; SV **field; HV *rv = newHV(); hv_store(rv, "err", 3, newSViv(INCOMPLETE), 0); while (p - raw < stop) { // HTTP 1.x Request Line extracted in METHOD->PATH->PROTOCOL states if (state == METHOD) { while (*p == ' ' || *p == '\t') p++; val = p; while (*p != ' ' && *p != '\t' && *p != '\0') p++; *p = '\0'; lowerCase(val); hv_store(rv, "method", 6, newSVpv(val, 0), 0); p++; state = PATH; continue; // loop condition checks bounds ;) } if (state == PATH) { while (*p <= 32 && *p > 0) p++; if (*p != '/' && *p != '*') { // must be an absolute path or * (RFC 2616) field = hv_fetch(rv, "err", 3, 0); sv_setiv(*field, BAD_PATH); break; } // turn path into an array AV *path = newAV(); val = p+1; if (*val <= 32) { av_push(path, newSVpv(p, 1)); p++; } else { while (*p > 32) p++; *p = '\0'; // check for query string if ((qs = strrchr(val, '?'))) { *qs = '\0'; qs = queryString(rv, ++qs); } while ((p = strchr(val, '/'))) { if (!*(p+1)) break;// path ends with '/' av_push(path, newSVpv(val, p-val)); val = ++p; } len = strlen(val); if (qs) p = qs + 1; else p = val + len + 1; if (val[len-1] == '/') len--; av_push(path, newSVpv(val, len)); } hv_store(rv, "path", 4, newRV_noinc((SV*)path), 0); state = PROTOCOL; continue; } if (state == PROTOCOL) { while (*p == ' ' || *p == '\t') p++; if (*p < 32) // no protocol hv_store(rv, "protocol", 8, newSVpv("???", 0), 0); else { val = p; while (*p > 32) p++; *(p++) = '\0'; hv_store(rv, "protocol", 8, newSVpv(val, 0), 0); } while (*p <= 32 && *p) p++; break; } } if (state >= PROTOCOL) { hv_store(rv, "headers", 7, newRV_noinc((SV*)parseHeaders(&p)), 0); while (*p == '\r' || *p == '\n') p++; if (*p && p < raw + stop) hv_store(rv, "body", 4, newSVpv(p, stop - (p-raw)), 0); field = hv_fetch(rv, "err", 3, 0); sv_setiv(*field, OK); } return rv; }