// http11_server3738_pohja.java // HTTP-palvelimen pohja import java.net.*; import java.util.Scanner; import java.util.Date; import java.io.*; import java.lang.Thread; import java.util.concurrent.atomic.AtomicInteger; public class http11_server3738_pohja { int debug = 1; // tulostaako palvelin jotain int port = 8080; // oletusportti AtomicInteger saikeidenMaara = null; public static void main(String[] args) { http11_server3738_pohja p = new http11_server3738_pohja(); if (args.length > 0) p.port = Integer.valueOf(args[0]); p.kuuntele(); } // main() public http11_server3738_pohja() { ; } // main server function public void kuuntele() { try { // initialize saikeidenMaara = new AtomicInteger(0); ServerSocket ss = new ServerSocket(port); report("Kuuntelee porttia " + port); while (true) { // slow down if more that 10 active connections if (saikeidenMaara.get() > 10) { report("kuuntele: " + saikeidenMaara.get() + " yhteyttä"); try { Thread.sleep(1*1000); } catch (Exception e) { } } // limit # of open connections while (saikeidenMaara.get() > 100) { report("kuuntele: " + saikeidenMaara.get() + " yhteyttä"); try { Thread.sleep(10*1000); } catch (Exception e) { } } // wait for connections Socket cs = ss.accept(); report("Accept"); // 20s aikakatkaisu cs.setSoTimeout(20*1000); // start new servant new YhteydenPalvelija(cs).start(); } } catch (Exception e) { report("Pääsäikeellä ongelmia " + e); } finally { } } // kuuntele() // report to stdout public synchronized void report(String s) { if (debug > 0) System.out.println(Thread.currentThread().getId() + " " + s); } // report // read file contents to byte array byte[] lueTiedostoTavuiksi(String tiedostonNimi, long maxPituus) { byte[] file = null; try { RandomAccessFile f = new RandomAccessFile(tiedostonNimi, "r"); long len = f.length(); if (len > maxPituus) return null; // Palvellaan vain lyhyötä tiedostoja // Oikeasti pidemmät palveltaisi paloina // Chunked file = new byte[(int)len]; f.read(file); f.close(); return file; } catch (IOException e) { report("Tiedoston luku ei onnistunut"); } return null; } // main serving thread public class YhteydenPalvelija extends Thread { Socket clientSock = null; // IO virrat BufferedReader in = null; PrintWriter out = null; // http-komennon osat String komento = null; String tiedostonNimi = null; String urls = null; boolean http11 = false; // 1.1 or 1.0 String sisallonTyyppi = null; // pyydetty tiedosto byte[] file = null; int fileLen = -1; // constructor inits character streams public YhteydenPalvelija(Socket cs) { super(); try { clientSock = cs; in = new BufferedReader(new InputStreamReader(clientSock.getInputStream())); out = new PrintWriter(clientSock.getOutputStream(), true); } catch (Exception e) { report("YhteydenPalvelija constructor ongelmissa: " + e); try { clientSock.close(); } catch (Exception ee) { } clientSock = null; } } // main service method public void run() { boolean ok = true; if (clientSock == null) return; saikeidenMaara.incrementAndGet(); try { report("Uusi yhteys: " + clientSock.getInetAddress() + ":" + clientSock.getPort()); while (true) { ok = luePyynto(); if (!ok) ; // TODO ok = tulkitseUrl(); if (! ok) ; // TODO ok = lueTiedosto(); if (! ok) { // vastausta tässä esimerkkinä report("404 NotFound"); out.println("HTTP/1.1 404 NotFound"); out.println(""); if (http11) continue; else { clientSock.close(); break; } } ok = lahetaVastauksenOtsikot(); ok = lahetaTiedosto(); if (!http11) { clientSock.close(); return; } } } catch (SocketTimeoutException te) { // asiakas hidasteli liikaa try { report("Aikakatkaisu, yhteys sulkeutuu"); out.println("HTTP/1.1 408 Request Time-out"); out.println("Connection: close"); out.println(""); out.flush(); clientSock.close(); } catch (Exception e) { } } catch (Exception e) { report("YhteydenPalvelija.run ongelmissa: " + e); e.printStackTrace(); } finally { saikeidenMaara.decrementAndGet(); report("Säie lopettaa"); } } // run() // alkellinen tiedostotyypin tunnistus public String arvaaSisallonTyyppi(String tiedostonNimi) { if (tiedostonNimi.endsWith(".html") || tiedostonNimi.endsWith(".htm")) return "text/html"; if (tiedostonNimi.endsWith(".txt")) return "text/plain"; if (tiedostonNimi.endsWith(".java")) return "text/x-java-source"; if (tiedostonNimi.endsWith(".png")) return "image/png"; if (tiedostonNimi.endsWith(".jpg")) return "image/jpg"; return "application/unknown"; } public boolean lahetaVastauksenOtsikot() { try { // TODO // lähetä ok, date, content-type, content-length ja tyhjä rivi out.println(); out.flush(); return true; } catch (Exception e) { report("lahetaVastauksenOtsikot ongelmissa " + e); } return false; } public boolean lahetaTiedosto() { try { // edellyttää, että saman soketin muut OutputStream:t ovat // tehneet .flush() ennen tätä OutputStream bout = new BufferedOutputStream(clientSock.getOutputStream()); // data palvelimelle bout.write(file); bout.flush(); return true; } catch (Exception e) { report("lahetaTiedosto ongelmissa " + e); return false; } } // lahetaTiedosto() boolean tulkitseUrl() { try { // hyvin simppeli versio, otetaan URL:n loppu viimeisen /-merkin jälkeen // poistetaan lopusta kaikki # ja ? jälkeen tiedostonNimi = urls.substring(urls.lastIndexOf("/")+1, urls.length()); int pois = tiedostonNimi.indexOf("#"); if (pois >= 0) tiedostonNimi = tiedostonNimi.substring(0, pois); pois = tiedostonNimi.indexOf("?"); if (pois >= 0) tiedostonNimi = tiedostonNimi.substring(0, pois); report("tiedostonNimi=" + tiedostonNimi); return true; } catch (Exception e) { report("CheckUrl:" + e); } return false; } boolean lueTiedosto() { file = lueTiedostoTavuiksi("./" + tiedostonNimi, 1000*1000L); if (file == null) return false; sisallonTyyppi = arvaaSisallonTyyppi(tiedostonNimi); return (file != null); } boolean luePyynto() throws SocketTimeoutException { try { // TODO // selaa GET -rivi, erota siitä GET tiedosto ja http-versio // skipataan kaikki loput headerit, // lopetetaan kun löydetään tyhjä rivi String rivi = in.readLine().trim(); while (rivi != null && !rivi.isEmpty()) rivi = in.readLine().trim(); return true; // TODO } catch (IOException e) { } return false; } // luePyynto() } // class YhteydenPalvelija } // class http11_server3738_pohja