SV *acceptConnection (SV *objref) { char error[1024]; HV *self = (HV*)SvRV(objref), *client = newHV(); SV **field = hv_fetch(self, "fd", 2, 0); int srv = SvIV(*field), sock, flags, yes = 1; struct sockaddr_in info; socklen_t len = sizeof(struct sockaddr_in); sock = accept(srv, (struct sockaddr*)&info, &len); if (sock == -1) { snprintf(error, 1024, "accept(): %s", strerror(errno)); hv_store(client, "sockerr", 7, newSVpv(error, 0), 0); close(sock); } else { setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(int)); hv_store(client, "fd", 2, newSViv(sock), 0); hv_store(client, "ip", 2, newSVpv(inet_ntoa(info.sin_addr), 0), 0); flags = fcntl(sock, F_GETFL, 0); if (flags == -1) { snprintf(error, 1024, "fcntl(F_GETFL): %s", strerror(errno)); hv_store(client, "sockerr", 7, newSVpv(error, 0), 0); close(sock); } else if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) { snprintf(error, 1024, "fcntl(F_SETFL O_NONBLOCK): %s", strerror(errno)); hv_store(client, "sockerr", 7, newSVpv(error, 0), 0); close(sock); } else { hv_store(client, "events", 6, newSViv(SPFD_READ), 0); hv_store(client, "state", 5, newSViv(READ_HEAD), 0); hv_store(client, "readerr", 7, newSViv(0), 0); hv_store(client, "errcount", 8, newSViv(0), 0); hv_store(client, "queue", 5, newRV_noinc((SV*)newAV()), 0); } } return sv_bless(newRV_noinc((SV*)client), gv_stashpv("Sloop::Socket", 0)); } #### 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; }