http.c (3821B)
1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include "last.h" 5 6 #define HTTPVER "HTTP/1.0" 7 8 char* 9 query(const char *path, const char *fmt) { 10 char *p, *q; 11 int i; 12 13 i = 0; 14 if(path) 15 i = strlen(path)+1; 16 17 p = malloc(i+strlen(fmt)+1); 18 for(q=p+i; *fmt; q++, fmt++) 19 if(*fmt == ' ') 20 *q = '&'; 21 else 22 *q = *fmt; 23 *q = '\0'; 24 25 if(i) { 26 memcpy(p, path, i-1); 27 p[i-1] = '?'; 28 } 29 setmalloctag(p, getcallerpc(&path)); 30 return p; 31 } 32 33 int 34 parseuri(char *uri, char **host, char **path) { 35 char *p, *q; 36 char c; 37 38 if(strncmp(uri, "http://", 7)) 39 return 1; 40 q = uri+7; 41 for(p=q; c = *p; p++) 42 if(c == '/') 43 break; 44 else if(c == ':') 45 *p = '!'; 46 if(c != '/') 47 return 1; 48 49 memmove(uri, "tcp!", 4); 50 memmove(uri+4, q, p-q); 51 uri[p-q+4] = '\0'; 52 53 *host = uri; 54 *path = p; 55 return 0; 56 } 57 58 int 59 hconv(Fmt *f) { 60 char *s, *p; 61 int ret, c; 62 63 ret = 0; 64 s = va_arg(f->args, char*); 65 for(; *s; s=p) { 66 for(p=s; (c = *p); p++) { 67 if(c & 0x80 || !isprint(c) || isspace(c)) 68 break; 69 if(c == '%' && !(f->flags & FmtComma)) 70 break; 71 switch(c) { 72 default: 73 continue; 74 case '<': 75 case '>': 76 case '{': 77 case '}': 78 case '|': 79 case '\\': 80 case ';': 81 case '&': 82 case '=': 83 case '@': 84 case '/': 85 case '?': 86 case '+': 87 break; 88 } 89 break; 90 } 91 if(p != s) 92 ret += fmtprint(f, "%.*s", p-s, s); 93 if(c) { 94 if(c == ' ') 95 ret += fmtstrcpy(f, "+"); 96 else 97 ret += fmtprint(f, "%%%02uX", (uchar)c); 98 p++; 99 } 100 } 101 return ret; 102 } 103 104 int 105 Hconv(Fmt *f) { 106 char *s, *p; 107 int ret, c; 108 109 ret = 0; 110 s = va_arg(f->args, char*); 111 for(; *s; s=p) { 112 for(p=s; (c = *p); p++) { 113 if(!isprint(c) || isspace(c) || c & 0x80) 114 break; 115 switch(c) { 116 default: 117 continue; 118 case '%': 119 case '<': 120 case '>': 121 case '{': 122 case '}': 123 case '|': 124 case '\\': 125 break; 126 } 127 break; 128 } 129 if(p != s) 130 ret += fmtprint(f, "%.*s", p-s, s); 131 if(c) { 132 ret += fmtprint(f, "%%%02uX", (uchar)c); 133 p++; 134 } 135 } 136 return ret; 137 } 138 139 void 140 httpinit(void) { 141 fmtinstall('h', hconv); 142 fmtinstall('H', Hconv); 143 } 144 145 #define error(...) do{werrstr(__VA_ARGS__); goto error;}while(0) 146 147 static char 148 Ebadresp[] = "Bad HTTP response code", 149 Ebadhdr[] = "Bad HTTP response header"; 150 151 Biobuf* 152 httpget(char *host, char *path) { 153 Biobuf *bi, bo; 154 char *s, *p; 155 ulong code; 156 int fd, n, nredir, c; 157 158 s = nil; 159 nredir = 0; 160 again: 161 if(nredir++ > 50) { 162 werrstr("redirect loop"); 163 return nil; 164 } 165 166 fd = dial(netmkaddr(host, "tcp", "http"), 0, 0, 0); 167 if(fd < 0) 168 return nil; 169 170 if(debug['h']) 171 print("GET %s %q\n", host, path); 172 Binit(&bo, fd, OWRITE); 173 Bprint(&bo, "GET %s %s\r\n", path, HTTPVER); 174 Bprint(&bo, "Host: %s\r\n", host); 175 Bprint(&bo, "User-Agent: http.c Plan 9\r\n"); 176 Bprint(&bo, "Accept: */*\r\n"); 177 Bprint(&bo, "\r\n"); 178 Bterm(&bo); 179 180 if(s) 181 free(s); 182 183 bi = Bfdopen(fd, OREAD); 184 185 s = Brdline(bi, '\n'); 186 if(s == nil) 187 error("%s: no data", Ebadresp); 188 189 n = BLINELEN(bi); 190 if(n < 2) 191 error(Ebadresp); 192 if(s[n-2] != '\r') 193 error(Ebadresp); 194 s[n-2] = '\0'; 195 196 if(debug['h']) 197 print("Resp: %s\n", s); 198 199 if(strncmp(s, HTTPVER " ", sizeof(HTTPVER))) 200 error("%s: bad version response", Ebadresp); 201 s += sizeof(HTTPVER); 202 203 code = strtoul(s, &p, 10); 204 if(p == s || !isspace(p[0])) 205 error("%s: non-numeric response code", Ebadresp); 206 207 if(code != 200 && code != 301 && code != 302) 208 error("http: %s", s); 209 210 for(;;) { 211 s = Brdline(bi, '\n'); 212 n = BLINELEN(bi); 213 if(n < 2 || s[n-2] != '\r') 214 error(Ebadhdr); 215 if(n == 2) 216 break; 217 s[n-2] = '\0'; 218 if(debug['h']) 219 print("Headr: %s\n", s); 220 p = strchr(s, ':'); 221 if(p == nil) 222 error(Ebadhdr); 223 *p++ = '\0'; 224 if((code == 301 || code == 302) && !cistrcmp(s, "Location")) { 225 while((c = *p) && isspace(c)) 226 p++; 227 s = strdup(p); 228 parseuri(s, &host, &path); 229 Bterm(bi); 230 goto again; 231 } 232 } 233 234 return bi; 235 236 error: 237 Bterm(bi); 238 return nil; 239 } 240