• Willkommen im Linux Club - dem deutschsprachigen Supportforum für GNU/Linux. Registriere dich kostenlos, um alle Inhalte zu sehen und Fragen zu stellen.

[gelöst] HTTP-Request über Termial

Wie kann man auf dem Terminal ein HTTP-Request (Anfrage) durchführen?
Also im Sinne von:
Code:
GET /infotext.html HTTP/1.1
Host: www.example.net

Das Programm lynx, welches ich bei der Rescherche gefunden habe funktioniert zwar als Terminal-Browser, jedoch habe ich nichts gefunden, wie ich selbst den Header für den Request schreiben kann und ebenso nur den reinen Code der Antwort (Response) erhalte.
Weiß da jemand mehr?
 
Verbinde über Telnet an den Port 80 des Servers.

Die Kommando Strings, welche übermittelt werden müssen findest du in der RFC zum HTTP Protokoll.

Möchtest du das an deinen Rechner zuhause ausprobieren kannst du dir auch einen primitiven Server selber bauen.
Hier ein kleines Beispiel in C das ich mal für eine Vorlesung programmiert habe.

Code:
#include <stdio.h>
#include <strings.h>
#include <string.h>
/*Bindings für Server funktionen*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>

//Verbindungsversuch annehmen
void akzeptieren(int socket, struct sockaddr_in adr_from,int *socket_from) {
	int size_adr = sizeof(adr_from);
	*socket_from = accept(socket,(struct sockaddr*) &adr_from,&size_adr);
	if(*socket_from == -1) {
		fprintf(stderr,"Fehler beim akzeptieren, %d\n",*socket_from);
	}
}

//Request des Browsers einlesen (noch nicht verarbeiten
void RequestEinlesen(int socket_from, char request[],int length) {
	int anzahl;
	anzahl = read(socket_from,request,length);
	if(anzahl <= -1)
		fprintf(stderr,"Fehler beim Lesen der Nachricht\n");
}

//Den Response header für den Browser abschicken
void ResponseHeader(char dateiname[],int length,int socket_from,int *fd) {
	char response[65536];
	bzero(response,sizeof(response));
	int pos = 0;
	//dateiendung ermitteln
	char *c = strrchr(dateiname,'.');
	pos = c - dateiname;
	//Nach dateiendung entscheiden welcher mime/typ (kann vermutlich besser gelöst werden)
	if (strncmp(&dateiname[pos+1],"html",5) == 0)
		strcpy(response,"HTTP/1.1 200\nContent-Type: text/html\n\n");
	else if (strncmp(&dateiname[pos+1],"htm",4) == 0)
		strcpy(response,"HTTP/1.1 200\nContent-Type: text/html\n\n");
	else if (strncmp(&dateiname[pos+1],"jpeg",4) == 0)
		strcpy(response,"HTTP/1.1 200\nContent-Type: image/jpeg\n\n");
	else if (strncmp(&dateiname[pos+1],"jpg",3) == 0)
		strcpy(response,"HTTP/1.1 200\nContent-Type: image/jpeg\n\n");
	else if (strncmp(&dateiname[pos+1],"png",3) == 0)
		strcpy(response,"HTTP/1.1 200\nContent-Type: image/png\n\n");
	else if (strncmp(&dateiname[pos+1],"gif",3) == 0)
		strcpy(response,"HTTP/1.1 200\nContent-Type: image/gif\n\n");
	else	{												//Falls mime/typ unbekannt
		*fd = -1;
		return;
	}
	//Schreib des response Header
	write(socket_from,response,strlen(response));
	bzero(response,sizeof(response));
}

//Den Request verarbeiten
void RequestBearbeiten(char request[],char dateiname[],int length,int socket_from) {
	int anzahl = 0;
	char website[65536];
	//Bisher nur get anfrage möglich
	if (strncmp(request,"GET",3) == 0) {//0 Wenn uebereinstimmung
		int fd;
		//Wenn keine spezielle datei angefordert index.html schicken
		if(request[5] == ' ') {
			fd = open("index.html",0);
			if(fd != -1)
				ResponseHeader("index.html",length,socket_from,&fd);
		}
		//Relative links sind verboten
		else if(strncmp(&request[5],"..",2 == 0)) {
			fd = -1;
		}

		//Hier aufpassen, bisher ist ein gang über einen absoluten pfad noch möglich
		else {
			int i = 0;
			while(request[i+5] != ' ') {
				dateiname[i] = request[i+5];
				++i;
			}
			dateiname[i] = '\0';
			fd = open(dateiname,0);
			if(fd != -1) {											//wenn datei nicht gefunden error
				ResponseHeader(dateiname,length,socket_from,&fd);
			}
		}
		if(fd == -1) {												//Der Verhasste 404 Fehler
			fd = open("404.html",0);
			write(socket_from,"HTTP/1.1 404\nContent-Type: text/html\n\n",38);
		}
		anzahl = read(fd,website,length);							  //Lokale datei auslesen (Tode fehlerbehandlung wenn -1)
		close(fd);											//Datei Schließen
		fprintf(stderr,"website ist: %s\n",website);					//4 Den Seitencode auf terminal ausgeben (Frage was ist das seltsame bei bildern?
		write(socket_from,website,anzahl);							//Den Response Body zum Browser schicken
		bzero(request,sizeof(request));							//Die arrays leeren um unerwünschte Artefakte zu verhindern
		bzero(website,sizeof(website));
	}
}

//Server Starten, Socket nummer beziehen, Socket binden, lauschen
void server_starten(int *socket_nummer,struct sockaddr_in *adr_from) {
	struct sockaddr_in adressinfo; //struktur vom typ sockaddr_in ueber socket.h
	int ergebnis;
	
	*socket_nummer = socket(AF_INET,SOCK_STREAM,0);				//Socket nummer vergeben
	//Adressinformationen angeben
	adressinfo.sin_family = AF_INET;
	adressinfo.sin_addr.s_addr = inet_addr("127.0.0.1");	
	adressinfo.sin_port = htons(80);
	
	//Socket an Prozess binden
	ergebnis = bind(*socket_nummer,(struct sockaddr*) &adressinfo,sizeof(adressinfo));	//Socket binden
	if(ergebnis == -1) {
	    fprintf(stderr,"Fehler beim binden des Sockets\n");
	}
	
	//Auf Port lauschen
	ergebnis = listen(*socket_nummer,5);								//Auf socket lauschen
	if(ergebnis == -1) {
		fprintf(stderr,"Fehler beim lauschen\n");
	}
}

int main(void) {
	//Variablen
	int socket_from;
	int socket_nummer;
	struct sockaddr_in adr_from;
	server_starten(&socket_nummer,&adr_from);					//den server bereit machen zur annahme von verbindungen

	//Variablen zur verarbeitung
	char request[65536];
	char dateiname[65536];
	int pid,status, length = 65534;							//Achtung hier war mal eine variable anzahl die auf 0 gesetzt wurde entfernt ohne kompilieren!
	while(1) {
		akzeptieren(socket_nummer,adr_from,&socket_from);			//Akzeptieren einer eingehenden Verbindung
		pid = fork();								        //Forken des Servers
		if(pid == 0) {									//Ab hier arbeitet Kind weiter
			//Einlesen des Requests
			RequestEinlesen(socket_from,request,length);			//Request einlesen, ACHTUNG!!! Ich habe hier anzahl entfernt, die funktion 
			printf("Der empfangende Request:\n\t%s\n",request);		//War mal eine int funktion, geändert ohne neu kompilieren!!!
			//Bearbeiten des Requests
			RequestBearbeiten(request,dateiname,length,socket_from);	//Bearbeiten des Request (hauptsache aller server funktionen)
			exit(0);									//Kind beenden
		}
		else if (pid > 0) {									//Hier arbeitet Papa
			waitpid(-1,&status,WNOHANG);
		}
		else  {										//Das sollte niemals eintreten!
			fprintf(stderr,"Da ist wohl ein Fehler passiert!");
		}
		close(socket_from);								//Bin mir nicht sicher ob das hier sinn macht!
	}
	close(socket_nummer);
	return 0;
}

Das Teil hat allerdings einige Einschränkungen, so ist der Fork Aufruf nicht optimal gelöst und er kann nur Antworten bis max 2^16 Bit schicken. Das Problem mit der beschränkten response größe ließe sich lösen, wenn du die while schleife leicht änderst.

Edit Edit:

Achja der verarbeitet nur get-Anfragen. ^^

Für ein simples angucken, was ein HTTP Server so macht reicht es aber auch so. ^^

Edit Edit Edit:

Du kannst aber natürlich auch einfach eine Seite aufrufen und das ganze mit einen Tool wie Wireshark mitschneiden. Das hat den Charme das du dann gleich angucken kannst was ein richtiger Browser mit einen Apache/Whatever Server zu besprechen hat.

Alternativ kannst du natürlich auch einfach die Entwickletools von Firefox nutzen.

Ich empfehle dennoch das Studium der RFCs, da steht genau drin was Sache ist.
 
Warum müssen es immer "Hochsprachen" sein? Die bash kann auch browsen.. :)

Code:
exec 5<> /dev/tcp/www.google.de/80
printf "GET / HTTP/1.0\n\n" >&5
cat <&5
exec 5>&-

Haveaniceday
 
haveaniceday schrieb:
Warum müssen es immer "Hochsprachen" sein? Die bash kann auch browsen.. :)

Code:
exec 5<> /dev/tcp/www.google.de/80
printf "GET / HTTP/1.0\n\n" >&5
cat <&5
exec 5>&-

Haveaniceday

Vorausgesetzt das die Option beim kompilieren gesetzt worden ist.
 

framp

Moderator
Teammitglied
haveaniceday schrieb:
...
Code:
exec 5<> /dev/tcp/www.google.de/80
printf "GET / HTTP/1.0\n\n" >&5
cat <&5
exec 5>&-
...
Nettes Feature mit der Redirection. Habe alles soweit im Netz zu exec und redirection beschrieben gefunden - nur
Code:
exec 5<>
nicht :-(. Haettest Du da einen Link fuer mich?

... Die bash kann auch browsen.. :)
Das Parsen des XML/HTML Ergebnisses wird dann wohl etwas aufwaendiger ... :roll:
 
konqueror info:/bash/Redirections
5<>"NAME" wird hier beschrieben: 3.6.10 Opening File Descriptors for Reading and Writing

man bash:
Code:
exec ...
          If command is not specified, any redirec-
          tions  take effect in the current shell, and the return
          status is 0.  If there  is  a  redirection  error,  the
          return status is 1.

Bei man ksh => exec liest sich das ganze besser für Filedeskriptoren.
Code:
        If arg is given, the command specified by the  arguments
         is  executed  in  place of this shell without creating a
         new  process.  Input/output  arguments  can  appear  and
         affect  the  current  process. If no arguments are given
         the effect of this command is to modify file descriptors
         as  prescribed  by the input/output redirection list. In
         this case, any file descriptor numbers  greater  than  2
         that  are  opened  with  this  mechanism are closed when
         invoking another program.

Hier ist auch ein Artikel zu dem Thema:
http://www.linuxjournal.com/content/more-using-bashs-built-devtcp-file-tcpip

Haveaniceday

PS: Parsen und scannen ist wirklich etwas grausam. Cookies sollten auch nicht einfach benutztbar sein.
 

framp

Moderator
Teammitglied
Danke haveaniceday. Da sieht man es mal wieder - man lernt nie aus - gottseidank :)
 
Oben