aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/smugfs/http.c
diff options
context:
space:
mode:
authorRuss Cox <rsc@swtch.com>2008-08-03 07:42:27 -0700
committerRuss Cox <rsc@swtch.com>2008-08-03 07:42:27 -0700
commit18824b586835525594cde126fbc90b8281d5af8b (patch)
treee8b69c05eda4543b6d2f3d30777abe6109b48b7f /src/cmd/smugfs/http.c
parent3d36f4437348227c5bad62587dc12b5fd4a3e95e (diff)
downloadplan9port-18824b586835525594cde126fbc90b8281d5af8b.tar.gz
plan9port-18824b586835525594cde126fbc90b8281d5af8b.tar.bz2
plan9port-18824b586835525594cde126fbc90b8281d5af8b.zip
smugfs(4): new program
Diffstat (limited to 'src/cmd/smugfs/http.c')
-rw-r--r--src/cmd/smugfs/http.c237
1 files changed, 237 insertions, 0 deletions
diff --git a/src/cmd/smugfs/http.c b/src/cmd/smugfs/http.c
new file mode 100644
index 00000000..9cf7f1d0
--- /dev/null
+++ b/src/cmd/smugfs/http.c
@@ -0,0 +1,237 @@
+#include "a.h"
+
+static char*
+haveheader(char *buf, int n)
+{
+ int i;
+
+ for(i=0; i<n; i++){
+ if(buf[i] == '\n'){
+ if(i+2 < n && buf[i+1] == '\r' && buf[i+2] == '\n')
+ return buf+i+3;
+ if(i+1 < n && buf[i+1] == '\n')
+ return buf+i+2;
+ }
+ }
+ return 0;
+}
+
+static int
+parseheader(char *buf, int n, HTTPHeader *hdr)
+{
+ int nline;
+ char *data, *ebuf, *p, *q, *next;
+
+ memset(hdr, 0, sizeof *hdr);
+ ebuf = buf+n;
+ data = haveheader(buf, n);
+ if(data == nil)
+ return -1;
+
+ data[-1] = 0;
+ if(data[-2] == '\r')
+ data[-2] = 0;
+ if(chattyhttp > 1){
+ fprint(2, "--HTTP Response Header:\n");
+ fprint(2, "%s\n", buf);
+ fprint(2, "--\n");
+ }
+ nline = 0;
+ for(p=buf; *p; p=next, nline++){
+ q = strchr(p, '\n');
+ if(q){
+ next = q+1;
+ *q = 0;
+ if(q > p && q[-1] == '\r')
+ q[-1] = 0;
+ }else
+ next = p+strlen(p);
+ if(nline == 0){
+ if(memcmp(p, "HTTP/", 5) != 0){
+ werrstr("invalid HTTP version: %.10s", p);
+ return -1;
+ }
+ q = strchr(p, ' ');
+ if(q == nil){
+ werrstr("invalid HTTP version");
+ return -1;
+ }
+ *q++ = 0;
+ strncpy(hdr->proto, p, sizeof hdr->proto);
+ hdr->proto[sizeof hdr->proto-1] = 0;
+ while(*q == ' ')
+ q++;
+ if(*q < '0' || '9' < *q){
+ werrstr("invalid HTTP response code");
+ return -1;
+ }
+ p = q;
+ q = strchr(p, ' ');
+ if(q == nil)
+ q = p+strlen(p);
+ else
+ *q++ = 0;
+ hdr->code = strtol(p, &p, 10);
+ if(*p != 0)
+ return -1;
+ while(*q == ' ')
+ q++;
+ strncpy(hdr->codedesc, q, sizeof hdr->codedesc);
+ hdr->codedesc[sizeof hdr->codedesc-1] = 0;
+ continue;
+ }
+ q = strchr(p, ':');
+ if(q == nil)
+ continue;
+ *q++ = 0;
+ while(*q != 0 && (*q == ' ' || *q == '\t'))
+ q++;
+ if(cistrcmp(p, "Content-Type") == 0){
+ strncpy(hdr->contenttype, q, sizeof hdr->contenttype);
+ hdr->contenttype[sizeof hdr->contenttype-1] = 0;
+ continue;
+ }
+ if(cistrcmp(p, "Content-Length") == 0 && '0' <= *q && *q <= '9'){
+ hdr->contentlength = strtoll(q, 0, 10);
+ continue;
+ }
+ }
+ if(nline < 1){
+ werrstr("no header");
+ return -1;
+ }
+
+ memmove(buf, data, ebuf - data);
+ return ebuf - data;
+}
+
+static char*
+genhttp(Protocol *proto, char *host, char *req, HTTPHeader *hdr, int wfd, int rfd, vlong rtotal)
+{
+ int n, m, total, want;
+ char buf[8192], *data;
+ Pfd *fd;
+
+ if(chattyhttp > 1){
+ fprint(2, "--HTTP Request:\n");
+ fprint(2, "%s", req);
+ fprint(2, "--\n");
+ }
+ fd = proto->connect(host);
+ if(fd == nil){
+ if(chattyhttp > 0)
+ fprint(2, "connect %s: %r\n", host);
+ return nil;
+ }
+
+ n = strlen(req);
+ if(proto->write(fd, req, n) != n){
+ if(chattyhttp > 0)
+ fprint(2, "write %s: %r\n", host);
+ proto->close(fd);
+ return nil;
+ }
+
+ if(rfd >= 0){
+ while(rtotal > 0){
+ m = sizeof buf;
+ if(m > rtotal)
+ m = rtotal;
+ if((n = read(rfd, buf, m)) <= 0){
+ fprint(2, "read: missing data\n");
+ proto->close(fd);
+ return nil;
+ }
+ if(proto->write(fd, buf, n) != n){
+ fprint(2, "write data: %r\n");
+ proto->close(fd);
+ return nil;
+ }
+ rtotal -= n;
+ }
+ }
+
+ total = 0;
+ while(!haveheader(buf, total)){
+ n = proto->read(fd, buf+total, sizeof buf-total);
+ if(n <= 0){
+ if(chattyhttp > 0)
+ fprint(2, "read missing header\n");
+ proto->close(fd);
+ return nil;
+ }
+ total += n;
+ }
+
+ n = parseheader(buf, total, hdr);
+ if(n < 0){
+ fprint(2, "failed response parse: %r\n");
+ proto->close(fd);
+ return nil;
+ }
+ if(hdr->contentlength >= MaxResponse){
+ werrstr("response too long");
+ proto->close(fd);
+ return nil;
+ }
+ if(hdr->contentlength >= 0 && n > hdr->contentlength)
+ n = hdr->contentlength;
+ want = sizeof buf;
+ data = nil;
+ total = 0;
+ goto didread;
+
+ while(want > 0 && (n = proto->read(fd, buf, want)) > 0){
+ didread:
+ if(wfd >= 0){
+ if(writen(wfd, buf, n) < 0){
+ proto->close(fd);
+ werrstr("write error");
+ return nil;
+ }
+ }else{
+ data = erealloc(data, total+n);
+ memmove(data+total, buf, n);
+ }
+ total += n;
+ if(total > MaxResponse){
+ proto->close(fd);
+ werrstr("response too long");
+ return nil;
+ }
+ if(hdr->contentlength >= 0 && total + want > hdr->contentlength)
+ want = hdr->contentlength - total;
+ }
+ proto->close(fd);
+
+ if(hdr->contentlength >= 0 && total != hdr->contentlength){
+ werrstr("got wrong content size %d %d", total, hdr->contentlength);
+ return nil;
+ }
+ hdr->contentlength = total;
+ if(wfd >= 0)
+ return (void*)1;
+ else{
+ data = erealloc(data, total+1);
+ data[total] = 0;
+ }
+ return data;
+}
+
+char*
+httpreq(Protocol *proto, char *host, char *req, HTTPHeader *hdr, int rfd, vlong rlength)
+{
+ return genhttp(proto, host, req, hdr, -1, rfd, rlength);
+}
+
+int
+httptofile(Protocol *proto, char *host, char *req, HTTPHeader *hdr, int fd)
+{
+ if(fd < 0){
+ werrstr("bad fd");
+ return -1;
+ }
+ if(genhttp(proto, host, req, hdr, fd, -1, 0) == nil)
+ return -1;
+ return 0;
+}