forked from dolphin-emu/dolphin
		
	git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@4 8ced0084-cf51-0410-be5f-012b33b47a6e
		
			
				
	
	
		
			310 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			310 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Under MIT licence from http://www.mindcontrol.org/~hplus/http-get.html
 | 
						|
 | 
						|
#if defined(WIN32)
 | 
						|
#include <winsock2.h>
 | 
						|
#include <windows.h>
 | 
						|
#include <time.h>
 | 
						|
#else
 | 
						|
#include <unistd.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#endif
 | 
						|
#include <assert.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <string.h>
 | 
						|
#include <stdlib.h>
 | 
						|
 | 
						|
#include "HTTP.h"
 | 
						|
#include "PortableSockets.h"
 | 
						|
 | 
						|
#if !defined(WIN32)
 | 
						|
static int strnicmp( char const * a, char const * b, int n) {
 | 
						|
	return strncasecmp( a, b, n );
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
	struct Chunk {
 | 
						|
		Chunk() {
 | 
						|
			next_ = 0;
 | 
						|
			size_ = 0;
 | 
						|
		}
 | 
						|
		Chunk * next_;
 | 
						|
		size_t size_;
 | 
						|
		char data_[ 30000 ];
 | 
						|
	};
 | 
						|
 | 
						|
	class HTTPQuery : public I_HTTPRequest {
 | 
						|
	public:
 | 
						|
		HTTPQuery() {
 | 
						|
			head_ = 0;
 | 
						|
			curRd_ = 0;
 | 
						|
			curWr_ = 0;
 | 
						|
			curOffset_ = 0;
 | 
						|
			toRead_ = 0;
 | 
						|
			complete_ = false;
 | 
						|
			gotLength_ = false;
 | 
						|
			socket_ = BAD_SOCKET_FD;
 | 
						|
		}
 | 
						|
		~HTTPQuery() {
 | 
						|
			if (socket_ != BAD_SOCKET_FD) {
 | 
						|
				::closesocket( socket_ );
 | 
						|
			}
 | 
						|
			Chunk * ch = head_;
 | 
						|
			while( ch != 0) {
 | 
						|
				Chunk * d = ch;
 | 
						|
				ch = ch->next_;
 | 
						|
				::free( d );
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		void setQuery( char const * host, unsigned short port, char const * url) {
 | 
						|
			if (strlen( url ) > 1536 || strlen( host ) > 256) {
 | 
						|
				return;
 | 
						|
			}
 | 
						|
			struct hostent * hent = gethostbyname( host );
 | 
						|
			if (hent == 0) {
 | 
						|
				complete_ = true;
 | 
						|
				return;
 | 
						|
			}
 | 
						|
			addr_.sin_family = AF_INET;
 | 
						|
			addr_.sin_addr = *(in_addr *)hent->h_addr_list[0];
 | 
						|
			addr_.sin_port = htons( port );
 | 
						|
			socket_ = ::socket( AF_INET, SOCK_STREAM, getprotobyname("tcp")->p_proto );
 | 
						|
			if (socket_ == BAD_SOCKET_FD) {
 | 
						|
				complete_ = true;
 | 
						|
				return;
 | 
						|
			}
 | 
						|
			int r;
 | 
						|
			r = ::connect( socket_, (sockaddr *)&addr_, sizeof( addr_ ) );
 | 
						|
			if (r < 0) {
 | 
						|
				complete_ = true;
 | 
						|
				return;
 | 
						|
			}
 | 
						|
			MAKE_SOCKET_NONBLOCKING( socket_, r );
 | 
						|
			if (r < 0) {
 | 
						|
				complete_ = true;
 | 
						|
				return;
 | 
						|
			}
 | 
						|
			char buf[2048];
 | 
						|
			sprintf( buf, "GET %s HTTP/1.0\r\nUser-Agent: Dolphin2.0\r\nAccept: */*\r\nHost: %s\r\nConnection: close\r\n\r\n",
 | 
						|
				url, host );
 | 
						|
			r = ::send( socket_, buf, int(strlen( buf )), NONBLOCK_MSG_SEND );
 | 
						|
			if (r != (int)strlen( buf )) {
 | 
						|
				complete_ = true;
 | 
						|
				return;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		void dispose() {
 | 
						|
			delete this;
 | 
						|
		}
 | 
						|
		void step() {
 | 
						|
			if (!complete_) {
 | 
						|
				if (!curWr_ || (curWr_->size_ == sizeof( curWr_->data_ ))) {
 | 
						|
					Chunk * c = new Chunk;
 | 
						|
					if (!head_) {
 | 
						|
						head_ = c;
 | 
						|
						curWr_ = c;
 | 
						|
					}
 | 
						|
					else {
 | 
						|
						curWr_->next_ = c;
 | 
						|
						curWr_ = c;
 | 
						|
					}
 | 
						|
				}
 | 
						|
				assert( curWr_ && (curWr_->size_ < sizeof( curWr_->data_ )) );
 | 
						|
				int r = ::recv( socket_, &curWr_->data_[curWr_->size_], int(sizeof(curWr_->data_)-curWr_->size_), 
 | 
						|
					NONBLOCK_MSG_SEND );
 | 
						|
				if (r > 0) {
 | 
						|
					curWr_->size_ += r;
 | 
						|
					assert( curWr_->size_ <= sizeof( curWr_->data_ ) );
 | 
						|
					if (gotLength_) {
 | 
						|
						if (toRead_ <= size_t(r)) {
 | 
						|
							toRead_ = 0;
 | 
						|
							complete_ = true;
 | 
						|
						}
 | 
						|
						else {
 | 
						|
							toRead_ -= r;
 | 
						|
						}
 | 
						|
					}
 | 
						|
					if (!gotLength_) {
 | 
						|
						char const * end = &head_->data_[head_->size_];
 | 
						|
						char const * ptr = &head_->data_[1];
 | 
						|
						while( ptr < end-1) {
 | 
						|
							if (ptr[-1] == '\n') {
 | 
						|
								if (!strnicmp( ptr, "content-length:", 15 )) {
 | 
						|
									ptr += 15;
 | 
						|
									toRead_ = strtol( ptr, (char **)&ptr, 10 );
 | 
						|
									gotLength_ = true;
 | 
						|
								}
 | 
						|
								else if (ptr[0] == '\r' && ptr[1] == '\n') {
 | 
						|
									size_t haveRead = end-ptr-2;
 | 
						|
									if (haveRead > toRead_) {
 | 
						|
										toRead_ = 0;
 | 
						|
									}
 | 
						|
									else {
 | 
						|
										toRead_ -= haveRead;
 | 
						|
									}
 | 
						|
									if (toRead_ == 0) {
 | 
						|
										complete_ = true;
 | 
						|
									}
 | 
						|
									break;
 | 
						|
								}
 | 
						|
							}
 | 
						|
							++ptr;
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
				else if (r < 0) {
 | 
						|
					if (!SOCKET_WOULDBLOCK_ERROR( SOCKET_ERRNO )) {
 | 
						|
						complete_ = true;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		bool complete() {
 | 
						|
			step();
 | 
						|
			return complete_;
 | 
						|
		}
 | 
						|
		void rewind() {
 | 
						|
			curRd_ = head_;
 | 
						|
			curOffset_ = 0;
 | 
						|
		}
 | 
						|
		size_t read( void * ptr, size_t size) {
 | 
						|
			step();
 | 
						|
			if (!head_) {
 | 
						|
				return 0;
 | 
						|
			}
 | 
						|
			if (!curRd_) {
 | 
						|
				curRd_ = head_;
 | 
						|
				assert( curOffset_ == 0 );
 | 
						|
			}
 | 
						|
			size_t copied = 0;
 | 
						|
			while( size > 0) {
 | 
						|
				assert( curRd_->size_ <= sizeof( curRd_->data_ ) );
 | 
						|
				size_t toCopy = curRd_->size_ - curOffset_;
 | 
						|
				if (toCopy > size) {
 | 
						|
					toCopy = size;
 | 
						|
				}
 | 
						|
				memcpy( ptr, &curRd_->data_[curOffset_], toCopy );
 | 
						|
				curOffset_ += toCopy;
 | 
						|
				assert( curOffset_ <= sizeof(curRd_->data_) );
 | 
						|
				ptr = ((char *)ptr)+toCopy;
 | 
						|
				size -= toCopy;
 | 
						|
				copied += toCopy;
 | 
						|
				if (curOffset_ == curRd_->size_) {
 | 
						|
					if (curRd_->next_ != 0) {
 | 
						|
						curRd_ = curRd_->next_;
 | 
						|
						curOffset_ = 0;
 | 
						|
					}
 | 
						|
					else {
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
			return copied;
 | 
						|
		}
 | 
						|
 | 
						|
		Chunk * head_;
 | 
						|
		Chunk * curRd_;
 | 
						|
		Chunk * curWr_;
 | 
						|
		size_t curOffset_;
 | 
						|
		size_t toRead_;
 | 
						|
		bool complete_;
 | 
						|
		bool gotLength_;
 | 
						|
		SOCKET socket_;
 | 
						|
		sockaddr_in addr_;
 | 
						|
	};
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
I_HTTPRequest * NewHTTPRequest( char const * url )
 | 
						|
{
 | 
						|
	static bool socketsInited;
 | 
						|
	if (!socketsInited) {
 | 
						|
		socketsInited = true;
 | 
						|
		INIT_SOCKET_LIBRARY();
 | 
						|
	}
 | 
						|
	if (strncmp( url, "http://", 7 )) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	url += 7;
 | 
						|
	char const * path = strchr( url, '/' );
 | 
						|
	if (!path) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	char name[ 256 ];
 | 
						|
	if (path-url > 255) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	strncpy( name, url, path-url );
 | 
						|
	name[path-url] = 0;
 | 
						|
	char * port = strrchr( name, ':' );
 | 
						|
	unsigned short iport = 80;
 | 
						|
	if (port) {
 | 
						|
		*port = 0;
 | 
						|
		iport = (unsigned short)( strtol( port+1, &port, 10 ) );
 | 
						|
	}
 | 
						|
	HTTPQuery * q = new HTTPQuery;
 | 
						|
	q->setQuery( name, iport, path );
 | 
						|
	return q;
 | 
						|
}
 | 
						|
 | 
						|
// TODO(ector):
 | 
						|
// Currently extremely bad implementation - busy waits!
 | 
						|
std::string HTTPDownloadText(const char *url)
 | 
						|
{
 | 
						|
	I_HTTPRequest *r = NewHTTPRequest(url);
 | 
						|
	int timeout = 10;
 | 
						|
	std::string text = "";
 | 
						|
	time_t t;
 | 
						|
	t = 0;
 | 
						|
	char buf[4096];
 | 
						|
	while (true) 
 | 
						|
	{
 | 
						|
		r->step();
 | 
						|
		size_t rd = r->read(buf, 4096);
 | 
						|
		if (rd > 0) 
 | 
						|
		{
 | 
						|
			buf[rd] = 0;
 | 
						|
			text += buf;
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			if (r->complete())
 | 
						|
				goto next;
 | 
						|
 | 
						|
			if (!t) {
 | 
						|
				time(&t);
 | 
						|
			}
 | 
						|
			else {
 | 
						|
				time_t t2;
 | 
						|
				time(&t2);
 | 
						|
				if (t2 > t + timeout) {
 | 
						|
					fprintf( stderr, "timeout\n");
 | 
						|
					goto next;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
next:
 | 
						|
    r->dispose();
 | 
						|
	return text;
 | 
						|
}
 | 
						|
 | 
						|
void UnittestMyNetwork()
 | 
						|
{
 | 
						|
	I_HTTPRequest * r = NewHTTPRequest( "http://www.cisco.com/" );
 | 
						|
	char buf[1024];
 | 
						|
	while( !r->complete()) {
 | 
						|
		r->step();
 | 
						|
		while( r->read( buf, sizeof( buf ) ) )
 | 
						|
			;
 | 
						|
	}
 | 
						|
	char buf2[100000];
 | 
						|
	r->rewind();
 | 
						|
	while( r->read( buf2, sizeof( buf2 ) ) )
 | 
						|
		;
 | 
						|
	r->dispose();
 | 
						|
}
 |