#include <uri.h>
#include <string.h>
#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h> // struct sockaddr_in
+#include <acess/sys.h> // _SysDebug
enum eProcols
{
const char **gasURLs;
int giNumURLs;
-int _ParseHeaderLine(char *Line, int State, size_t *Size);
+ int main(int argc, char *argv[]);
+void streamToFile(int Socket, const char *OutputFile, int bAppend, size_t bytes_wanted, char *inbuf, size_t ofs, size_t len);
+ int _ParseHeaderLine(char *Line, int State, size_t *Size);
+void writef(int fd, const char *format, ...);
int main(int argc, char *argv[])
{
// Do the download
for( int i = 0; i < giNumURLs; i ++ )
{
- char *outfile;
+ char *outfile = NULL;
tURI *uri = URI_Parse(gasURLs[i]);
struct addrinfo *addrinfo;
printf("Proto: %s, Host: %s, Path: %s\n", uri->Proto, uri->Host, uri->Path);
- if( uri->Path[0] == '\0' )
+ if( uri->Path[0] == '\0' || uri->Path[strlen(uri->Path)-1] == '/' )
outfile = "index.html";
- else
- outfile = strrchr(uri->Path, '/') + 1;
- if( !outfile )
- outfile = uri->Path;
+ else {
+ outfile = strrchr(uri->Path, '/');
+ if( !outfile )
+ outfile = uri->Path;
+ else
+ outfile += 1;
+ }
if( strcmp(uri->Proto, "http") == 0 ) {
proto = PROTO_HTTP;
continue ;
}
- rv = getaddrinfo(uri->Host, NULL, NULL, &addrinfo);
+ if( proto != PROTO_HTTP ) {
+ fprintf(stderr, "TODO: Support protocols other than HTTP\n");
+ free(uri);
+ continue ;
+ }
+
+ rv = getaddrinfo(uri->Host, "http", NULL, &addrinfo);
if( rv != 0 ) {
fprintf(stderr, "Unable to resolve %s: %s\n", uri->Host, gai_strerror(rv));
continue ;
{
int bSkipLine = 0;
// TODO: Convert to POSIX/BSD
- int sock = Net_OpenSocket(addr->ai_family, addr->ai_addr, "tcpc");
- if( sock == -1 ) continue ;
+ // NOTE: using addr->ai_addr will break for IPv6, as there is more info before the address
+
+ void *addr_data;
+ switch(addr->ai_family)
+ {
+ case AF_INET: addr_data = &((struct sockaddr_in*)addr->ai_addr)->sin_addr; break;
+ case AF_INET6: addr_data = &((struct sockaddr_in6*)addr->ai_addr)->sin6_addr; break;
+ default: addr_data = NULL; break;
+ }
+ if( !addr_data )
+ continue ;
+ printf("Attempting [%s]:80\n", Net_PrintAddress(addr->ai_family, addr_data));
+
+ int sock = Net_OpenSocket_TCPC(addr->ai_family, addr_data, 80);
+ if( sock == -1 ) {
+ continue ;
+ }
+
+ _SysDebug("Connected as %i", sock);
+
writef(sock, "GET /%s HTTP/1.1\r\n", uri->Path);
writef(sock, "Host: %s\r\n", uri->Host);
// writef(sock, "Accept-Encodings: */*\r\n");
writef(sock, "\r\n");
// Parse headers
- char inbuf[BUFSIZ+1];
- size_t offset = 0;
+ char inbuf[16*1024];
+ size_t offset = 0, len = 0;
int state = 0;
- size_t bytes_seen = 0;
size_t bytes_wanted = -1; // invalid
+
+ inbuf[0] = '\0';
while( state == 0 || state == 1 )
{
- if( offset == sizeof(inbuf) ) {
+ if( offset == BUFSIZ ) {
bSkipLine = 1;
offset = 0;
}
- // TODO: Handle -1 return
- offset += read(sock, inbuf + offset, sizeof(inbuf) - offset);
- inbuf[offset] = 0;
-
+ inbuf[len] = '\0';
+
char *eol = strchr(inbuf, '\n');
// No end of line char? read some more
- if( eol == NULL ) continue ;
- // Update write offset
- offset = eol + 1 - inbuf;
-
+ if( eol == NULL ) {
+ // TODO: Handle -1 return
+ len += read(sock, inbuf + offset, BUFSIZ - 1 - offset);
+ continue ;
+ }
+
+ // abuse offset as the end of the string
+ offset = (eol - inbuf) + 1;
+
// Clear EOL bytes
*eol = '\0';
// Nuke the \r
if( eol - 1 >= inbuf )
eol[-1] = '\0';
-
if( !bSkipLine )
state = _ParseHeaderLine(inbuf, state, &bytes_wanted);
-
- memmove( inbuf, inbuf + offset, sizeof(inbuf) - offset );
- offset = 0;
+
+ // Move unused data down in memory
+ len -= offset;
+ memmove( inbuf, inbuf + offset, BUFSIZ - offset );
+ offset = len;
}
if( state == 2 )
{
- int outfd = open(outfile, O_WR|O_CREAT, 0666);
- if( outfd != -1 )
- {
- while( bytes_seen < bytes_wanted )
- {
- // Abuses offset as read byte count
- offset = read(sock, inbuf, sizeof(inbuf));
- write(outfd, inbuf, offset);
- bytes_seen += offset;
- }
- close(outfd);
- }
+ _SysDebug("RXing %i bytes to '%s'", bytes_wanted, outfile);
+ streamToFile(sock, outfile, 0, bytes_wanted, inbuf, len, sizeof(inbuf));
}
+ _SysDebug("Closing socket");
close(sock);
break ;
}
free(uri);
}
+
+ return 0;
+}
+
+void streamToFile(int Socket, const char *OutputFile, int bAppend, size_t bytes_wanted, char *inbuf, size_t len, size_t buflen)
+{
+ int outfd = open(OutputFile, O_WRONLY|O_CREAT, 0666);
+ if( outfd == -1 ) {
+ fprintf(stderr, "Unable to open '%s' for writing\n", OutputFile);
+ return ;
+ }
+
+ int64_t start_time = _SysTimestamp()-1;
+ size_t bytes_seen = 0;
+ // Write the remainder of the buffer
+ do
+ {
+ write(outfd, inbuf, len);
+ bytes_seen += len;
+ _SysDebug("%i/%i bytes done", bytes_seen, bytes_wanted);
+ printf("%7i/%7i KiB (%ikB/s) \r",
+ bytes_seen/1024, bytes_wanted/1024,
+ bytes_seen/(_SysTimestamp() - start_time)
+ );
+ fflush(stdout);
+
+ if( bytes_seen < bytes_wanted )
+ len = read(Socket, inbuf, buflen);
+ } while( bytes_seen < bytes_wanted && len > 0 );
+ int64_t stop_time = _SysTimestamp();
+ close(outfd);
+ printf("%i KiB done in %is (%i kB/s)\n",
+ bytes_seen/1024,
+ (int)(stop_time - start_time)/1000,
+ bytes_seen/(stop_time - start_time)
+ );
}
int _ParseHeaderLine(char *Line, int State, size_t *Size)
{
+ _SysDebug("Header - %s", Line);
// First line (Status and version)
if( State == 0 )
{
// Body lines
else
{
+ char *value;
char *colon = strchr(Line, ':');
if(colon == NULL) return 1;
*colon = '\0';
+ value = colon + 2;
if( strcmp(Line, "Content-Length") == 0 ) {
- *Size = atoi(colon + 1);
+ *Size = atoi(value);
}
else {
- printf("Ignorning header '%s' = '%s'\n", Line, colon+1);
+ printf("Ignorning header '%s' = '%s'\n", Line, value);
}
return 1;
}
}
+void writef(int fd, const char *format, ...)
+{
+ va_list args;
+ size_t len;
+
+ va_start(args, format);
+ len = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+
+ char data[len + 1];
+ va_start(args, format);
+ vsnprintf(data, len+1, format, args);
+ va_end(args);
+
+ write(fd, data, len);
+}
+