2 * Acess2 Command-line HTTP Client (wget)
14 #include <netinet/in.h> // struct sockaddr_in
15 #include <acess/sys.h> // _SysDebug
27 int main(int argc, char *argv[]);
28 void streamToFile(int Socket, const char *OutputFile, int bAppend, size_t bytes_wanted, char *inbuf, size_t ofs, size_t len);
29 int _ParseHeaderLine(char *Line, int State, size_t *Size);
30 void writef(int fd, const char *format, ...);
32 int main(int argc, char *argv[])
35 gasURLs = malloc( (argc - 1) * sizeof(*gasURLs) );
38 for(int i = 1; i < argc; i ++ )
43 gasURLs[giNumURLs++] = arg;
45 else if( arg[1] != '-') {
54 for( int i = 0; i < giNumURLs; i ++ )
57 tURI *uri = URI_Parse(gasURLs[i]);
58 struct addrinfo *addrinfo;
61 fprintf(stderr, "'%s' is not a valid URL", gasURLs[i]);
65 printf("Proto: %s, Host: %s, Path: %s\n", uri->Proto, uri->Host, uri->Path);
67 if( uri->Path[0] == '\0' || uri->Path[strlen(uri->Path)-1] == '/' )
68 outfile = "index.html";
70 outfile = strrchr(uri->Path, '/');
77 if( strcmp(uri->Proto, "http") == 0 ) {
80 else if( strcmp(uri->Proto, "https") == 0 ) {
85 fprintf(stderr, "Unknown protocol '%s'\n", uri->Proto);
90 if( proto != PROTO_HTTP ) {
91 fprintf(stderr, "TODO: Support protocols other than HTTP\n");
96 rv = getaddrinfo(uri->Host, "http", NULL, &addrinfo);
98 fprintf(stderr, "Unable to resolve %s: %s\n", uri->Host, gai_strerror(rv));
102 for( struct addrinfo *addr = addrinfo; addr != NULL; addr = addr->ai_next )
105 // TODO: Convert to POSIX/BSD
106 // NOTE: using addr->ai_addr will break for IPv6, as there is more info before the address
109 switch(addr->ai_family)
111 case AF_INET: addr_data = &((struct sockaddr_in*)addr->ai_addr)->sin_addr; break;
112 case AF_INET6: addr_data = &((struct sockaddr_in6*)addr->ai_addr)->sin6_addr; break;
113 default: addr_data = NULL; break;
118 printf("Attempting [%s]:80\n", Net_PrintAddress(addr->ai_family, addr_data));
120 int sock = Net_OpenSocket_TCPC(addr->ai_family, addr_data, 80);
125 _SysDebug("Connected as %i", sock);
127 writef(sock, "GET /%s HTTP/1.1\r\n", uri->Path);
128 writef(sock, "Host: %s\r\n", uri->Host);
129 // writef(sock, "Accept-Encodings: */*\r\n");
130 writef(sock, "User-Agent: awget/0.1 (Acess2)\r\n");
131 writef(sock, "\r\n");
135 size_t offset = 0, len = 0;
137 size_t bytes_wanted = -1; // invalid
141 while( state == 0 || state == 1 )
143 if( offset == BUFSIZ ) {
149 char *eol = strchr(inbuf, '\n');
150 // No end of line char? read some more
152 // TODO: Handle -1 return
153 len += read(sock, inbuf + offset, BUFSIZ - 1 - offset);
157 // abuse offset as the end of the string
158 offset = (eol - inbuf) + 1;
163 if( eol - 1 >= inbuf )
167 state = _ParseHeaderLine(inbuf, state, &bytes_wanted);
169 // Move unused data down in memory
171 memmove( inbuf, inbuf + offset, BUFSIZ - offset );
177 _SysDebug("RXing %i bytes to '%s'", bytes_wanted, outfile);
178 streamToFile(sock, outfile, 0, bytes_wanted, inbuf, len, sizeof(inbuf));
181 _SysDebug("Closing socket");
192 void streamToFile(int Socket, const char *OutputFile, int bAppend, size_t bytes_wanted, char *inbuf, size_t len, size_t buflen)
194 int outfd = open(OutputFile, O_WRONLY|O_CREAT, 0666);
196 fprintf(stderr, "Unable to open '%s' for writing\n", OutputFile);
200 int64_t start_time = _SysTimestamp();
201 size_t bytes_seen = 0;
202 // Write the remainder of the buffer
205 write(outfd, inbuf, len);
207 _SysDebug("%i/%i bytes done", bytes_seen, bytes_wanted);
208 printf("%7i/%7i KiB (%iKiB/s)\r",
209 bytes_seen/1024, bytes_wanted/1024,
210 bytes_seen/(_SysTimestamp() - start_time)
214 len = read(Socket, inbuf, buflen);
215 } while( bytes_seen < bytes_wanted && len > 0 );
217 printf("%i KiB done in %is (%i KiB/s)\n",
219 (_SysTimestamp() - start_time)/1000,
220 bytes_seen/(_SysTimestamp() - start_time)
224 int _ParseHeaderLine(char *Line, int State, size_t *Size)
226 _SysDebug("Header - %s", Line);
227 // First line (Status and version)
230 // HACK - assumes HTTP/1.1 (well, 9 chars before status)
231 switch( atoi(Line + 9) )
237 fprintf(stderr, "Unknown HTTP status - %s\n", Line + 9);
242 else if( Line[0] == '\0' )
250 char *colon = strchr(Line, ':');
251 if(colon == NULL) return 1;
255 if( strcmp(Line, "Content-Length") == 0 ) {
259 printf("Ignorning header '%s' = '%s'\n", Line, value);
266 void writef(int fd, const char *format, ...)
271 va_start(args, format);
272 len = vsnprintf(NULL, 0, format, args);
276 va_start(args, format);
277 vsnprintf(data, len+1, format, args);
280 write(fd, data, len);