Protocolul HTTP

Protocolul Hypertext Transfer Protocol (HTTP) este un protocol de nivel aplicatie pentru comunicatii in sisteme distribuite. Acest protocol sta la baza World Wide Web (WEB).

Java pune la dispozitia programatorului o serie de clase cu ajutorul carora pot fi accesate resursele WEB.

Citirea unei resurse URL

URL este acronimul pentru Uniform Resource Locator si reprezinta o referinta (adresa) la o resursa aflata pe Internet. Aceasta este în general un fisier reprezentând o pagina Web sau o imagine, însa un URL poat referi si interogari la baze de date, rezultate ale unor comenzi (programe), etc.

Pentru a accesa o resursă din Internet identificată printr-un URL, în java, primul pas este de a crea un obiect URL.

URL utcn = new URL("http://www.utcluj.ro/index.html");

Clasa URL conţine metode prin intermediul cărora se pot afla toate componentele unui URL: getProtocol(), getPort(), getHost(), getFile(), getRef().

Odată creat obiectul URL, se utilizează metoda openStream() pentru a deschide un flux de intrare, prin intermediul căruia se citeşte conţinutul respectivului URL.

import java.net.*;
import java.io.*;
 
public class SourceViewer {
 
	public static void main (String[] args) {
 
		String url = "http://www.utcluj.ro";
 
			try {
 
			    //Open the URL for reading
				URL u = new URL(url);
				InputStream in = u.openStream( );
				// buffer the input to increase performance
				in = new BufferedInputStream(in);
				// chain the InputStream to a Reader
				Reader r = new InputStreamReader(in);
				int c;
				while ((c = r.read( )) != -1) {
				    System.out.print((char) c);
				}
			}
			catch (MalformedURLException e) {
			System.err.println(args[0] + " is not a parseable URL");
			}
			catch (IOException e) {
			System.err.println(e);
			}
 
	} // 
} // end SourceViewer
in.close();

Accesarea bidirectionala a unei resurse URL

Daca se doreşte să se realizeze mai mult decât citirea conţinutului unui URL, atunci din cadrul clasei URL se poate apela metoda openConnection(). Această metodă va returna un obiect URLConnection. Acest obiect va putea fi utilizat pentru operaţii de scriere, citire precum şi interogări către un URL.

URLConnection connection = utcn.openConnection();

In continuare este prezentat un scurt program care citeşte conţinutul unui URL utilizând clasa URLConnection.

import java.net.*;
import java.io.*;
 
public class URLConnectionReader {
    public static void main(String[] args) throws Exception {
        URL utcluj = new URL(http://www.utcluj.ro);
        URLConnection con = yahoo.openConnection();
        BufferedReader in = new BufferedReader(
                                new InputStreamReader(
                                con.getInputStream()));
        String inputLine;
 
        while ((inputLine = in.readLine()) != null) 
            System.out.println(inputLine);
        in.close();
    }
}

Multe pagini HTML conţin form-uri (zone de text, butoane etc.) care permit introducerea de date şi transmiterea acestora către server. După completarea câmpurilor se apasă un buton iar browserul web va transmite datele către URL-ul corespunzător. URL-ul care recepţionează datele este un script cgi-bin. Acesta procesează datele şi transmite către client un răspuns, care de obicei este o altă pagină HTML.

Programele java pot interacţiona cu script-urile cgi-bin. Ele trebuie să fie capabile să scrie date către un URL. Acest lucru se realizează utilizând clasa URLConnection.

In listingul următor este exemplificat modul în care un program java poate interacţiona cu un script cgi-bin.

try{      
       String data=””;                   
        data += URLEncoder.encode("nume") + "=" + URLEncoder.encode("Adi”);
        data += "&" + URLEncoder.encode("nota”) + "=" + URLEncoder.encode("8.50”);
 
        URL url = new URL(“http://193.226.6.117:80/test/test.php");
        URLConnection conn = url.openConnection();
        conn.setDoOutput(true);
        OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
        wr.write(data);
        wr.flush();
 
        // Get the response
        BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String line;
        while ((line = rd.readLine()) != null) {
            System.out.println(line);
         }
 
        rd.close();
        wr.close();
}catch(Exception ex){ex.printStackTrace();}

In listingul anterior se presupune că există un script cgi-bin test.php, către care programul java trimite două variabile : nume=adi şi nota=8.50 . Scriptul cgi va citi respectivele variabile şi va transmite către programul java un răspuns.

Implementarea unui browser

In listingul urmator este prezentata o varianta simplificata a unui browser implementat utilizand limbajul Java.

import javax.swing.*;
import java.awt.*;
import javax.accessibility.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.net.*;
import java.io.*;
import java.awt.event.*;
 
 
public class Browser extends JPanel {
  Browser() {
    setLayout (new BorderLayout (5, 5));
    final JEditorPane jt = new JEditorPane();
    final JTextField input =
      new JTextField("http://127.0.0.1:8080");
    // read-only
    jt.setEditable(false);
    // follow links
    jt.addHyperlinkListener(new HyperlinkListener () {
      public void hyperlinkUpdate(
          final HyperlinkEvent e) {
        if (e.getEventType() ==
            HyperlinkEvent.EventType.ACTIVATED) {
          SwingUtilities.invokeLater(new Runnable() {
            public void run() {
              // Save original
              Document doc = jt.getDocument();
              try {
                URL url = e.getURL();
                jt.setPage(url);
                input.setText (url.toString());
              } catch (IOException io) {
                JOptionPane.showMessageDialog (
                  Browser.this, "Can't follow link",
                  "Invalid Input",
                   JOptionPane.ERROR_MESSAGE);
                jt.setDocument (doc);
              }
            }
          });
        }
      }
    });
    JScrollPane pane = new JScrollPane();
    pane.setBorder (
      BorderFactory.createLoweredBevelBorder());
    pane.getViewport().add(jt);
    add(pane, BorderLayout.CENTER);
 
    input.addActionListener (new ActionListener() {
      public void actionPerformed (ActionEvent e) {
        try {
          jt.setPage (input.getText());
        } catch (IOException ex) {
          JOptionPane.showMessageDialog (
            Browser.this, "Invalid URL",
            "Invalid Input",
            JOptionPane.ERROR_MESSAGE);
        }
      }
    });
    add (input, BorderLayout.SOUTH);
  }
}
 
import javax.swing.*;
 
public class Start extends JFrame{
public Start(){
                        Browser b = new Browser();
                        getContentPane().add(b);
                        pack();
                        setVisible(true);
                        }
 
public static void main(String args[])
            {
            Start s = new Start();
            }
}
 

Aplicatie server http

In această sectiune este creat un server HTTP care poate răspunde la cereri GET. Functia main() din cadrul clasei HttpServer startaează un fir de execuţie. In cadrul acestui fir se instanţiază un obiect ServerSocket şi se incepe ascultarea portului 80, care este portul standard pentru protocolul HTTP.

In momentul în care apare o cerere (un client se conectează pe portul 80) metoda accept() va returna un obiect Socket. In continuare se crează un obiect PrecesRequest (care este de tip fir de excutie), care va primi ca parametru, obiectul Socket returnat de metoda accept(). După crearea obiectului ProcesRequest, serverul revine în aşteptare şi va putea servi alţi clienţi. Clasa ProcesRequest implementează o versiune simplificată a protocolului HTTP. In cadrul constructorului clasei ProcesRequest se crează fluxurile de intrare \ ieşire, după care este startat firul de execuţie. In cadrul firului de execuţie este analizată cererea primită de la client , şi în cazul în care aceasta este o cerere validă de tip GET, atunci se va transmite către client resursa solicitată.

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package httpserver;
 
import java.io.*;
import java.net.*;
import java.util.StringTokenizer;
 
public class HttpServer extends Thread
{
            //portul standard
            private final static int PORT = 80;
 
            private final String iniContext="d:/temp";
            private boolean alive;
 
            private ServerSocket ss;
            //constructor
            HttpServer()throws Exception{
                        System.out.println("Start server http.");
                        ss = new ServerSocket(PORT);
                        alive=true;
                        start();
            }
 
            public void run(){
                        while(alive){
 
                                    //asteapta conexiuni
                                    try{
                                    System.out.println("Server asteapta...");
                                    new ProcesRequest(ss.accept(),iniContext);
 
                                    }catch(IOException e){System.err.println("EROARE CONECTARE:"+e.getMessage());}
                                    //..reia bucla de asteptare dupa ce am creat un fir pentru client
                        }
                        System.out.println("STOP SERVER");
            }
            public static void main(String[] args)throws Exception
            {
                        try{
                        new HttpServer();
                        }catch(Exception e){e.printStackTrace();}
            }
 
}
 
class ProcesRequest extends Thread
{
            private PrintWriter outStr;
            private BufferedReader inStr;
            private Socket s;
            private DataOutputStream dout;
            private String iniContext;
 
            ProcesRequest(Socket s, String iContext){
                        try{
                                    outStr = new PrintWriter(new OutputStreamWriter(s.getOutputStream()),true);
                                    inStr = new BufferedReader(new InputStreamReader(s.getInputStream()));
                                    dout = new DataOutputStream(s.getOutputStream());
                                    iniContext = iContext;
                                    this.s = s;
                                    start();
                        }catch(IOException e)
                        {System.err.println("EROARE CONECTARE: "+e.getMessage());}
            }
 
            public void run(){
                        try{
                                    String fileName=null;
                                    String request = inStr.readLine();
                                    System.out.print(request);
                                    if(request.lastIndexOf("GET")==0) fileName = interpretGET(request);
                                    else throw new Exception("BAU");
                                    byte[] data = readFile(fileName);
                                    dout.write(data);
                                    dout.flush();
 
                        }
                        catch(IOException e){outStr.println("<HTML><BODY><P>403 Forbidden<P></BODY></HTML>");}
                        catch(Exception e2){outStr.println("<HTML><BODY><P>"+e2.getMessage()+"<P></BODY></HTML>");}
                        finally{
                                    try{s.close();}catch(Exception e){}
                        }
            }
 
            private String interpretGET(String rqst) throws Exception{
                        StringTokenizer strT = new StringTokenizer(rqst);
                        String tmp="";
                        String fileN=iniContext;
                        tmp=strT.nextToken();
                        if(!tmp.equals("GET")) throw new Exception("Comanda GET invalida .");
 
                        tmp=strT.nextToken();
                        if((tmp.equals("/")) || (tmp.endsWith("/"))) {
                                    fileN = fileN+tmp+"index.htm";
                                    System.err.println("CERERE:"+fileN);
                                    return fileN;
                        }
 
                        fileN = fileN+ tmp;
                        System.err.println("CERERE:"+fileN);
                        return fileN;
            }
 
            private byte[] readFile(String fileN) throws Exception{
                        fileN.replace('/','\\');
                        File f = new File(fileN);
                        if(!f.canRead()) throw new Exception("Fisierul "+fileN+" nu poate fi citit");
                        FileInputStream fstr = new FileInputStream(f);
                        byte[] data = new byte[fstr.available()];
                        fstr.read(data);
                        return data;
            }
}

Pentru verificarea programului anterior se va crea un folder cu numele temp pe driverul d si se va adauga in el un fisier html (de exemplu index.html). Se deschide un browser de internet si se incarca pagina http://localhost/index.html. In acest moment browserul va comunica cu serverul html anterior creat si va cere resursa index.html. Serverul va cauta in directorul d:/temp resursa index.html si o va transmite catre browser.