/* * Verifica se url informada e http ou https */ void webcrawler::parse_link(string &url, bool &secure) { size_t aux; if ((aux = str_find_ci(url, "http://")) != string::npos) //Verifica se o início do link é http:// { secure = false; //Marca como http url.erase(aux, 7); //Remove está string } else if ((aux = str_find_ci(url, "https://")) != string::npos) //Verifica se o início do link é https:// { secure = true; //Marca como https url.erase(aux, 8); //Remove está string } }
/* * Filtra os novos links da página recebida */ void httpclient::parse_page() { size_t pos = 0, pos_max = 0, aux = 0; //Auxiliares para manipulação de strings s_trace trace; //Estrutura local que será utilizada como base para inserção de novas urls no vetor char symbol[2] = ""; if (deep >= webc->get_deep()) //Verifica se a profundidade atual é igual ou maior que a limite return; trace.deep = deep + 1; //Links desta página serão de profundidade K+1 while (pos != string::npos) { //Busca novos links <a href= if ((pos = str_find_ci(page, "<a ", pos)) != string::npos) { //Verifica se existe alguma tag 'a' após a posição atual pos_max = page.find(">", pos); //Procura o final desta tag para evitar transtornos pos = str_find_ci(page, string("href="), pos); //Procura o atributo 'href' da tag 'a' encontrada if (pos < pos_max) { //Verifica se o atributo 'href' foi encontrado até o limite da tag symbol[0] = page[pos + 5]; //Salva o simbolo após =, pois pode ser ' ou " pos = page.find(symbol, pos); //Procura o início do valor do atributo aux = page.find(symbol, pos + 1); //Procura o fim do valor do atributo trace.url = page.substr(pos + 1, aux - pos - 1); //Obtém a nova url a ser colocada no vetor if (trace.url.substr(0, 11) != "javascript:" && trace.url[0] != '#') //Verifica se o link não é um javascript ou algum outro truque { if ((aux = str_find_ci(trace.url, "http://")) != string::npos) //Verifica se o início do link é http:// trace.url.erase(aux, 7); //Remove está string aux = trace.url.find("/"); //Procura o caracter '/' if (aux == string::npos) //Verifica se o caracter '/' não foi encontrado trace.url = host + "/" + trace.url; //Então a url será: host_atual+/+nova_url else if (aux == 0) //Verifica se o caracter foi encontrado na primeira posição do novo link trace.url = host + trace.url; //Então a url será: host_atual+nova_url else if ((aux = trace.url.substr(0, aux - 1).find(".")) == 0 || aux == string::npos) //Verifica se o caracter '.' foi encontrado na primeira posição do novo link ou não foi encontrado trace.url = host + "/" + trace.url; //Então a url será: host_atual+/+nova_url while ((aux = trace.url.find("\r\n")) != string::npos) //Procura \r\n perdidos no meio do link trace.url.erase(aux, 2); //Remove os \r\n do meio do link webc->push_trace(new s_trace(trace)); //Adiciona a nova url no vetor } } } if (pos != string::npos) pos = pos_max; } pos = 0; while (pos != string::npos) { //Busca novos links <img src= if ((pos = str_find_ci(page, "<img", pos)) != string::npos) { //Verifica se existe alguma tag 'img' após a posição atual pos_max = page.find(">", pos); //Procura o final desta tag para evitar transtornos pos = str_find_ci(page, string("src="), pos); //Procura o atributo 'src' da tag 'img' encontrada if (pos < pos_max) { //Verifica se o atributo 'src' foi encontrado até o limite da tag symbol[0] = page[pos + 4]; //Salva o simbolo após =, pois pode ser ' ou " pos = page.find(symbol, pos); //Procura o início do valor do atributo aux = page.find(symbol, pos + 1); //Procura o fim do valor do atributo trace.url = page.substr(pos + 1, aux - pos - 1); //Obtém a nova url a ser colocada no vetor if ((aux = str_find_ci(trace.url, "http://")) != string::npos) //Verifica se o início do link é http:// trace.url.erase(aux, 7); //Remove está string aux = trace.url.find("/"); //Procura o caracter '/' if (aux == string::npos) //Verifica se o caracter '/' não foi encontrado trace.url = host + "/" + trace.url; //Então a url será: host_atual+/+nova_url else if (aux == 0) //Verifica se o caracter foi encontrado na primeira posição do novo link trace.url = host + trace.url; //Então a url será: host_atual+nova_url else if ((aux = trace.url.substr(0, aux - 1).find(".")) == 0 || aux == string::npos) //Verifica se o caracter '.' foi encontrado na primeira posição do novo link ou não foi encontrado trace.url = host + "/" + trace.url; //Então a url será: host_atual+/+nova_url while ((aux = trace.url.find("\r\n")) != string::npos) //Procura \r\n perdidos no meio do link trace.url.erase(aux, 2); //Remove os \r\n do meio do link webc->push_trace(new s_trace(trace)); //Adiciona a nova url no vetor } } if (pos != string::npos) pos = pos_max; } }
/* * Cria a conexão com o dominio desejado */ void *httpclient::make_connection(void *client_ptr) { httpclient *me = (httpclient *)client_ptr; //Ponteiro para o cliente atual struct addrinfo hints, *result, *rp; //Estruturas auxiliares para criação do socket string &url = me->get_surl(); //Url base do cliente atual string host; //Host para conexão int ret = 0; //Valor a ser retornado pela thread int s; //SocketID size_t pos; //Auxiliar para manipulação de strings #ifdef WIN32 WSADATA wsaData; DWORD timeOut; timeOut = 5000;//5 segundos #else struct timeval timeOut; timeOut.tv_usec = 0; timeOut.tv_sec = 5; //5 segundos de timeout #endif me->count(); //Incrementa o contador de execuções do cliente #ifdef WIN32 ret = WSAStartup(MAKEWORD(1, 1), &wsaData); //Inicia o uso do dll winsock, com socket versão 1.1 if (ret != 0) { webc->write_error(to_string(me->get_id()) + "|WSAStartup:" + url + " ~ " + host + " ~ " + to_string(WSAGetLastError())); webc->push_client(me); //Coloca o cliente de volta na lista } #endif memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; //Permite que ip seja IPV4 ou IPV6 hints.ai_socktype = SOCK_STREAM; //Socket STREAM para utilização do TCP hints.ai_flags = 0; hints.ai_protocol = IPPROTO_TCP; //Define que o protocolo utilizado será TCP if ((pos = str_find_ci(url, "/")) != string::npos) host = url.substr(0, pos); //Caso exista um diretório na url base o mesmo é filtrado else host = url; if ((pos = str_find_ci(host, "www.")) != string::npos && pos == 0) host = host.substr(4); //Caso seja encontrado o www., o mesmo também é removido s = getaddrinfo(host.c_str(), "80", &hints, &result); //Obtém lista de estruturas de endereços if (s != 0) { webc->write_error(to_string(me->get_id()) + "|Getaddrinfo:" + url + " ~ " + host + " ~ " + gai_strerror(s)); webc->push_client(me); //Coloca o cliente de volta na lista pthread_exit((void*)-1); } // Tenta conectar em toda a lista de enderecos fornecidos pelo getaddrinfo, ate conseguir se conectar a algum for (rp = result; rp != NULL; rp = rp->ai_next) { s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (s == -1) continue; if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1) break;//Sucesso na conexão closesocket(s); } if (rp == NULL) { //Nao foi possivel se conectar a nenhum dos enderecos webc->write_error(to_string(me->get_id()) + "|Socket:" + url + " ~ " + host + " ~ " + gai_strerror(s)); webc->push_client(me); // Coloca este cliente de volta na lista pthread_exit((void*)-2); } freeaddrinfo(result); //Nao e mais necessario setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeOut, sizeof(timeOut)); //Define um timeout para o recebimento me->set_socket(s); //Passa o socket para a classe if (me->send_req() >= 0 && me->read_page() >= 0) //Envia requisição e recebe a resposta { me->parse_page(); //Verifica novas urls na pagina recebida } webc->push_client(me); //Coloca este cliente de volta na lista closesocket(s); //Fecha o socket utilizado #ifdef WIN32 WSACleanup(); #endif pthread_exit((void*)ret); //Coloca o valor do erro no retorno return 0; }
/* * Recebe a resposta da requisição enviada */ int httpclient::read_page() { ofstream out_file(webc->create_dir("webs/" + host + "/" + dir).c_str(), ofstream::binary); //Cria arquivo de saída para o conteúdo ofstream header_file(webc->create_dir("webs/" + host + "/" + dir + ".header").c_str(), ofstream::binary); //Cria arquivo de saída para o cabeçalho int tmp; //Auxiliar para controle do recebimento bool content = false, chunked = false; size_t pos; //Auxiliar para manipulação de strings tmp = recv(s, buffer, sizeof(buffer)-1, 0); //Solicita o recebimento de dados page = string(""); header = string(""); while (tmp > 0) { //Enquanto tmp maior que 0 continua tentando receber if (content) { //Verifica se o cabeçalho já foi todo recebido page.append(buffer, tmp); //Insere a parte do conteúdo recebida na string de conteúdo } else { //O cabeçalho ainda não foi totalmente recebido char *ptr = strstr(buffer, "\r\n\r\n"); //Procura o final do cabeçalho GET HTTP/1.1 if (ptr) { header.append(buffer, ptr - buffer + 4); //Insere na string de cabeçalho a parte deste pacote que faz parte do cabeçalho /* Verificação de conteúdo codificado */ pos = str_find_ci(header, "Transfer-Encoding:"); //Procura a string que identifica a codificação de transferencia if (pos != string::npos) { //Caso exista a codificação é analisada qual o tipo da mesma string encoding = header.substr(pos + 19, header.find("\r\n", pos) - (pos + 19)); if (encoding == "chunked") //Verifica se é do tipo chunked chunked = true; } while (*ptr == '\r' || *ptr == '\n') ptr++; tmp = tmp - (ptr - buffer); //É obtido então quantos bytes do pacote atual fazem parte do corpo da mensagem if (tmp >= 0) //Verifica se o número de bytes de corpo remanescentes é válido page.append(ptr, tmp); //Insere na string de corpo a parte deste pacote que faz parte do corpo content = true; //Confirma que a partir deste momento todos os bytes serão de conteúdo } else { header.append(buffer, tmp); //Escreve todos os bytes recebidos na string de cabeçalho } } memset(buffer, 0, sizeof(buffer)-1); tmp = recv(s, buffer, sizeof(buffer)-1, 0); //Solicita o recebimento de dados buffer[tmp] = '\0'; } if (errno != 0) { //Verifica se houve algum erro no recv if (chunked) { //Caso o Transfer-Encoding utilizado seja chunked size_t old = 0, next = 0, pos; //Auxiliares pos = page.find("\r\n", 0) + 2; //Procura o final da informação do chunk next = stoi(page, nullptr, 16); //Obtém a quantidade de bytes do chunk page.erase(old, pos - old); //Remove a informação do chunk do conteúdo da page old = next; while (next) { pos = page.find("\r\n", old + 2) + 2; //Procura o final da informação do chunk if (page[old + 2] != '0') next = stoi(&page[old + 2], nullptr, 16); //Obtém a quantidade de bytes do chunk else next = 0; //Chunk final page.erase(old, pos - old); //Remove a informação do chunk do conteúdo da page old += next; } } } header_file.write(page.c_str(), page.size()); //Escreve no arquivo do cabeçalho a string que contém o cabeçalho out_file.write(page.c_str(), page.size()); //Escreve no arquivo do corpo a string que contém o corpo header_file.close(); //Fecha os arquivos out_file.close(); return 0; }