Share
Embed
新しいプログラミング言語の学び方 HTTPサーバーを作って学ぶ Java, Scala, Clojure
JJUG CCC 2017 Fallでの発表資料です。
Shunsuke Tadokoro
November 24, 2017
Transcript
149 slides
-
DDD@H ͓͢͠Δ͜ͱ w ͳͥ)551αʔόʔ w 4DBMB $MPKVSFʹ͍ͭͯ w )551αʔόʔΛ࡞Γͳ͕Βֶ΅͏
w ͬ͘͟ΓΞʔΩςΫνϟ w ֤ݴޠͰ࣮ͯ͠ΈΔ w 4PDLFUͷѻ͍ w ਖ਼نදݱ w Ϧιʔεͷ։์ w ฒྻॲཧ w จࣈྻͷѻ͍ w ·ͱΊ -
DDD@H 8FCαʔϏεʹ%#͕͋Δ w ͱΓ͋͑ͣ)Ͱྑ͍͔ʜ w .Z42-8PSL#FODIͰ࡞ͬͨ%%-͕ྲྀͤͳ͍ʂ w ϩʔΧϧͷ.Z42-͕ݹ͍ɻCSFXVQEBUFʂ w ͍VQHSBEFͩͬͨ
w %PDLFS ͱ͔͍͏ͭͰ .Z42-Λಈ͔͢ͷ͕ φ͍ΜͰ͔͢Ͷ w IFSPLV1PTUHSF42-ͳΜͩͬͨ -
DDD@H 8FCαʔϏεʹը໘͕͋Δ w ͱΓ͋͑ͣదʹCPPUTUSBQͰ࡞Ζ͏ʂ w ΄͏΄͏ɺ4FNBOUJD6*ͷ΄͏͕ΠϚυΩͬΆ͍Β͍͠ w ςϯϓϨʔτΤϯδϯʁ࣌41"Ͱ͠ΐ w ੜ$44ਏ͍͔Β4BTTʹ͠Α͏
w 8FCQBDL CBCFMMPBEFS ͳʹͦΕ͓͍͍͠ͷʁ w EJW͕தԝʹدΒͳ͍ -
DDD@H 1ZUIPOͷ4JNQMF)5514FSWFS w 1ZUIPOͷ4JNQMF)5514FSWFSΛࢀߟʹ͢Δͷ̋ w ܥ w ܥ $ python
-m SimpleHTTPServer $ python -m http.server -
DDD@H 4DBMB w +BWBͱͷ૬ޓӡ༻ੑ w γʔϜϨεͳݺͼग़͠ɺ+BWBඪ४ϥΠϒϥϦͷ࠶ར༻ w ؆ܿੑ w লུՄೳͳߏจɺܕਪɺڧྗͳඪ४ϥΠϒϥϦ
w நͷߴ͍ίʔυɺ৽੍͍͠ޚߏจΛఆٛͰ͖Δදݱྗ w 8IBUͷڧௐɺ)PXͷӅณ w ੩తܕ͚ w ݕূՄೳੑɺϦϑΝΫλͷ͢͠͞ɺυΩϡϝϯτੑ -
DDD@H $MPKVSF w -JTQ w จ๏͕গͳ͍ɺσʔλͱͯ͠ͷίʔυ w ؔܕϓϩάϥϛϯάͷͨΊͷݴޠ w ୈҰڃؔɺΠϛϡʔλϒϧͳσʔλߏɺ࠶ؼతͳϧʔϓ
w +BWBͱͷ૬ޓӡ༻ੑ w ฒߦॲཧͷͨΊʹઃܭ w 3&1-Λ׆͔ͨ͠ΠϯλϥΫςΟϒΠϯΫϦϝϯλϧͳ ։ൃ -
DDD@H $MPKVSFΧοίͷछྨ͕ଟ͘ɺಡΈ͍͢ ͨͱ͑ؔͷҾ $PNNPO-JTQ 4DIFNF $MPKVSF (defun square (x) (*
x x)) (define (square x) (* x x)) (defn square [x] (* x x)) -
DDD@H ࢛ଇԋࢉ 12 + 40 10 - 1 2 *
3 5 / 2 +BWB 4DBMB (+ 12 40) (- 10 1) (* 2 3) (/ 5 2) ; -> 5/2 Λѻ͏Ratioܕ $MPKVSF -
DDD@H มએݴ int x = 10; +BWB val x =
10 val x: Int = 10 4DBMB (def x 10) (let [y 10] (+ y 3)) ; yletͷׅހ͚ͩͰࢀরͰ͖Δ $MPKVSF -
DDD@H ϝιουɾؔએݴ public int f(int x) { return x +
1; } +BWB (defn f [x] (+ x 1)) $MPKVSF def f(x: Int) = x + 1 4DBMB -
DDD@H ࠓճ༻ҙͨ͠)551αʔόʔ w ىಈ͠ɺMPDBMIPTUͷಛఆͷϙʔτͰ)551ϦΫΤετΛͪड͚Δ w ରԠ͢Δ)551ϦΫΤετϝιου(&5ͷΈ ͦΕҎ֎ͷϝιου(&5ͱΈͳ͢ w QVCMJDσΟϨΫτϦΑΓ্ͷ֊ͷϦΫΤετʹ 'PSCJEEFOΛฦ͢
w Ϧιʔεͷ.*.&֎෦ϑΝΠϧͰઃఆͰ͖Δ w ϦΫΤετΛϒϩοΫ͠ͳ͍ʢϚϧνεϨουʣ w ,FFQ"MJWF͠ͳ͍ɻίωΫγϣϯDMPTF͢Δ w )551$BDIF͠ͳ͍ɻฦ͞ͳ͍ -
DDD@H ىಈ w +BWB NWODPNQJMF NWOFYFDKBWB w 4DBMB TCUSVO w
$MPKVSF MFJOSVO -
DDD@H ͭ͘Γʹ͍ͭͯͬ͘͟Γͱ ├── MimeDetector.java ├── Request.java ├── RequestHandler.java ├── RequestParser.java
├── Response.java ├── SimpleJavaHttpServer.java └── WorkerThread.java ΞϓϦέʔγϣϯͷΤϯτϦʔϙΠϯτ 3FRVFTU *OQVU4USFBN 3FTQPOTF 0VUQVU4USFBN ϦΫΤετͷύʔε ϦΫΤετͷϋϯυϦϯά Ϩεϙϯεͷ8SJUF -
DDD@H ͭ͘Γʹ͍ͭͯͬ͘͟Γͱ ├── MimeDetector.java ├── Request.java ├── RequestHandler.java ├── RequestParser.java
├── Response.java ├── SimpleJavaHttpServer.java └── WorkerThread.java )551ϦΫΤετΛύʔε͢Δ 3FRVFTU *OQVU4USFBN 3FTQPOTF 0VUQVU4USFBN ϦΫΤετͷύʔε ϦΫΤετͷϋϯυϦϯά Ϩεϙϯεͷ8SJUF -
DDD@H ͭ͘Γʹ͍ͭͯͬ͘͟Γͱ ├── MimeDetector.java ├── Request.java ├── RequestHandler.java ├── RequestParser.java
├── Response.java ├── SimpleJavaHttpServer.java └── WorkerThread.java ϦΫΤετΛද͢ΦϒδΣΫτ 3FRVFTU *OQVU4USFBN 3FTQPOTF 0VUQVU4USFBN ϦΫΤετͷύʔε ϦΫΤετͷϋϯυϦϯά Ϩεϙϯεͷ8SJUF -
DDD@H ͭ͘Γʹ͍ͭͯͬ͘͟Γͱ ├── MimeDetector.java ├── Request.java ├── RequestHandler.java ├── RequestParser.java
├── Response.java ├── SimpleJavaHttpServer.java └── WorkerThread.java ϦΫΤετ͔ΒϨεϙϯεΛੜ 3FRVFTU *OQVU4USFBN 3FTQPOTF 0VUQVU4USFBN ϦΫΤετͷύʔε ϦΫΤετͷϋϯυϦϯά Ϩεϙϯεͷ8SJUF -
DDD@H ͭ͘Γʹ͍ͭͯͬ͘͟Γͱ ├── MimeDetector.java ├── Request.java ├── RequestHandler.java ├── RequestParser.java
├── Response.java ├── SimpleJavaHttpServer.java └── WorkerThread.java ϨεϙϯεΛද͢ΦϒδΣΫτ 3FRVFTU *OQVU4USFBN 3FTQPOTF 0VUQVU4USFBN ϦΫΤετͷύʔε ϦΫΤετͷϋϯυϦϯά Ϩεϙϯεͷ8SJUF -
DDD@H ͭ͘Γʹ͍ͭͯͬ͘͟Γͱ ├── MimeDetector.java ├── Request.java ├── RequestHandler.java ├── RequestParser.java
├── Response.java ├── SimpleJavaHttpServer.java └── WorkerThread.java )551ϨεϙϯεΛ8SJUF 3FRVFTU *OQVU4USFBN 3FTQPOTF 0VUQVU4USFBN ϦΫΤετͷύʔε ϦΫΤετͷϋϯυϦϯά Ϩεϙϯεͷ8SJUF -
DDD@H 4PDLFUͷѻ͍ // αʔόʔιέοτͷੜ ServerSocket serverSocket = new ServerSocket(8080); while
(true) { // ଓΛͪड͚Δɻଓ͞ΕΔ·ͰϒϩοΫɻ Socket socket = serverSocket.accept(); InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); ... } 4JNQMF+BWB)UUQ4FSWFSKBWBʢൈਮʣ -
DDD@H 4PDLFUͷѻ͍ // αʔόʔιέοτͷੜ val serverSocket = new ServerSocket(8080) while
(true) { // ଓΛͪड͚Δɻଓ͞ΕΔ·ͰϒϩοΫ val socket = serverSocket.accept val in = s.getInputStream val out = s.getOutputStream ... } 4JNQMF)UUQ4FSWFSTDBMBʢൈਮʣ -
DDD@H 4PDLFUͷѻ͍ (let [server-socket (new ServerSocket 8080)] (while true (let
[socket (.accept server-socket) in (.getInputStream socket) out (.getOutputStream socket)] ...))) DPSFDMKʢൈਮʣ -
DDD@H ໊લ͖άϧʔϓ w ਖ਼نදݱͷάϧʔϓʹ໊લΛ͚Δ͜ͱ͕Ͱ͖Δ w άϧʔϓͷॱং͕มΘͬͨͱͯ͠औΓग़͢ॲཧͦͷ·· import java.util.regex.*; String regex
= "(?<year>\\d+)/(?<month>\\d+)/(?<day>\\d+)"; Pattern p = Pattern.compile(regex); Matcher m = p.matcher("2017/11/18"); if (m.find()) { System.out.println(m.group("year")); // 2017 System.out.println(m.group("month")); // 11 System.out.println(m.group("day")); // 18 } -
DDD@H w ϦΫΤετϥΠϯͷ֤ཁૉʹ໊લΛ͚ͯநग़ public static Pattern requestLinePattern = Pattern.compile("(?<method>.*) (?<path>.*?)
(?<version>.*?)"); public Request fromInputStream(InputStream in){ BufferedReader reader = new BufferedReader(new InputStreamReader(in)); String requestLine = reader.readLine(); Matcher matcher = requestLinePattern.matcher(requestLine); if (!matcher.find()) return null; String method = matcher.group("method"); String targetPath = matcher.group("path"); String httpVersion = matcher.group("version"); return new Request(method, targetPath, httpVersion); } ໊લ͖άϧʔϓ 3FRVFTU1BSTFSKBWBʢൈਮʣ -
DDD@H ύλʔϯϚον w +BWBͷTXJUDIจʹࣅ͍ͯΔ͕ɺΑΓॊೈͰڧྗ w ʹҰக͢Δ͔͚ͩͰͳ͘ɺܕߏͰذ 0 match { case
0 => "Zero" // "Zero" case _ => "Other" } List(1, 2, 3) match { case List(_, x, _) => x // 2 case _ => -1 } 1 match { x: Int => s"$xInt" // 1Int x: String => s"$xString" x => s"$x???" } -
DDD@H ύλʔϯϚον w ਖ਼نදݱΦϒδΣΫτʹରͯ͠Ϛονͤ͞ΒΕΔ val pattern = "(.+)/(.+)/(.+)".r //.rͰregexܕ "2017/11/18"
match { case pattern(y, m, d) => s"${y}${m}݄${d}" case _ => "???" } -
DDD@H ύλʔϯϚον val pattern = "(.+) (.+) (.+)".r requestLine match
{ case pattern(method, path, version) => Some(Request(method, path, version)) case _ => None } 3FRVFTU1BSTFSTDBMBʢൈਮʣ -
DDD@H 0QUJPOܕ requestLine match { case pattern(method, path, version) =>
Some(Request(method, path, version)) case _ => None } w +BWBͰ͍͏0QUJPOBM w 4PNFͱ/POF͔ΒͳΔܕ w ͕͋Δ͔ͳ͍͔͔Βͳ͍ঢ়ଶΛද͢ w ͕ଘࡏ͢Δ͔ͷνΣοΫΛڧ੍Ͱ͖Δ -
DDD@H SFpOE w ਖ਼نදݱʹϚονͨ͠จࣈྻΛऔಘ w ҾʹάϧʔϓԽͨ͠ਖ਼نදݱΦϒδΣΫτΛ͢ͱɺ ઌ಄ʹϚονͨ͠จࣈྻશମɺҎ߱ʹΩϟϓνϟ͞ΕͨจࣈྻͷϕΫλʔ ;; #"" java.util.regex.PatternͷϦςϥϧ
(re-find #"(.+)/(.+)/(.+)" "2017/11/18") ;; => ["2017/11/18" "2017" "11" "18"] ;; restͰઌ಄Ҏ֎ͷཁૉΛऔಘ (rest (re-find #"(.+)/(.+)/(.+)" "2017/11/18")) ;; => ("2017" "11" "18") -
DDD@H [JQNBQ w ڧྗͳίϨΫγϣϯϥΠϒϥϦ$MPKVSFͷಛ w [JQNBQͭͷίϨΫγϣϯ͔Β.BQΛ࡞Δ w $MPKVSFͰΫϥεΑΓ.BQΛΉ (zipmap [:a
:b :c] [1 2 3]) ;; => {:a 1, :b 2, :c 3} -
DDD@H SFpOE [JQNBQ (let [line (.readLine reader)] (zipmap [:method :path
:version] (rest (re-find #"(.+) (.+) (.+)" line)))) ;; => {:method "GET", :path "/", :version "HTTP/1.1"} SFRVFTU@QBSTFSDMKʢൈਮʣ -
DDD@H SFpOE [JQNBQ (let [line (.readLine reader)] (zipmap [:method :path
:version] (rest (re-find #"(.+) (.+) (.+)" line)))) ;; => {:method "GET", :path "/", :version "HTTP/1.1"} SFRVFTU@QBSTFSDMKʢൈਮʣ <NFUIPEQBUIWFSTJPO> <(&5)551> -
DDD@H SFpOE [JQNBQ (let [line (.readLine reader)] (zipmap [:method :path
:version] (rest (re-find #"(.+) (.+) (.+)" line)))) ;; => {:method "GET", :path "/", :version "HTTP/1.1"} SFRVFTU@QBSTFSDMKʢൈਮʣ <NFUIPEQBUIWFSTJPO> <(&5)551> -
DDD@H 5SZXJUI3FTPVSDFT try (InputStream in = new FileInputStream(file)) { //
Կ͔ϦιʔεΛѻ͏ॲཧ } // try۟Λൈ͚ͨΒclose w USZ۟Λൈ͚ͨΒࣗಈͰDMPTF w ରKBWBJP$MPTFBCMF KBWBMBOH"VUP$MPTFBCMFͷ࣮Ϋϥε -
DDD@H -PBO1BUUFSO w ʮआΓͨΒฦ͢ʯΛ࣮֬ʹߦ͏ w ੍ޚߏͷΑ͏ʹݟ͔͚࣮ͤͯϝιου val reader = new
BufferedReader(...) using(reader) { r => // readerΛͬͨԿ͔ͷॲཧ } // ϒϩοΫΛൈ͚ͨΒreaderclose͞Ε͍ͯΔ -
DDD@H -PBO1BUUFSOͷ࣮ def using[A, R <: Closeable](resource: R)(f: R =>
A): A = { try { f(resource) } finally { resource.close() } } -
DDD@H -PBO1BUUFSOͷ࣮ def using[A, R <: Closeable](resource: R)(f: R =>
A): A = { try { f(resource) } finally { resource.close() } } ܕม3$MPTFBCMF -
DDD@H -PBO1BUUFSOͷ࣮ def using[A, R <: Closeable](resource: R)(f: R =>
A): A = { try { f(resource) } finally { resource.close() } } 3ܕͷԿ͔Λड͚औΓɺԿΒ͔ͷܕ"Λฦؔ͢ΛҾʹͱΔ -
DDD@H -PBO1BUUFSOͷ࣮ def using[A, R <: Closeable](resource: R)(f: R =>
A): A = { try { f(resource) } finally { resource.close() } } Ϧιʔεʹରͯؔ͠Λద༻ -
DDD@H -PBO1BUUFSOͷ࣮ def using[A, R <: Closeable](resource: R)(f: R =>
A): A = { try { f(resource) } finally { resource.close() } } ࠷ޙʹDMPTF -
DDD@H ׅހ ͱதׅހ\^ "hoge".startsWith{"ho"} // ͋·ΓΒͳ͍ "hoge".replace{"ho", "fu"} // Ͱ͖ͳ͍
"hoge" map { c => c.someFunc ... } // Α͘Δ w Ҿ͕Ұ͚ͭͩͷ߹ɺׅހΛதׅހͰॻ͍ͯྑ͍ w ҾʹؔΛ͢߹ɺதׅހΛ͏͜ͱ͕Α͋͘Δ -
DDD@H -PBO1BUUFSO using(socket) { s => val in = s.getInputStream
val out = s.getOutputStream val request = parser.fromInputStream(in) val response = request.map(handleRequest) response.foreach(_.writeTo(out)) } 4JNQMF)UUQ4FSWFSTDBMBʢൈਮʣ -
DDD@H XJUIPQFO (with-open [s socket] (-> (request-parser/from-input-stream (.getInputStream s)) (request-handler/handle-request)
(response-writer/write (.getOutputStream s)))) DPSFDMKʢൈਮʣ -
DDD@H 5ISFBE class HogeThread extends Thread { public void run()
{ // Կ͔ඇಉظʹ࣮ߦ͍ͨ͠ॲཧ } } HogeThread h = new HogeThread(); h.start(); class FugaRunnable implements Runnable { public void run() { // Կ͔ඇಉظʹ࣮ߦ͍ͨ͠ॲཧ } } FugaRunnable f = new Thread(new FugaRunnable()); f.start(); 5ISFBEΛFYUFOET3VOOBCMFΛJNQMFNFOUT -
DDD@H 5ISFBE public class WorkerThread extends Thread { private Socket
socket; private RequestParser parser; private RequestHandler handler; public WorkerThread( Socket socket, RequestParser parser, RequestHandler handler) { ... 8PSLFS5ISFBEKBWBʢൈਮʣ 4JNQMF+BWB)UUQ4FSWFSKBWBʢൈਮʣ Thread worker = new WorkerThread(socket, parser, handler); worker.start(); -
DDD@H &YFDVUPS4FSWJDF ExecutorService cachedPool = Executors.newCachedThreadPool(); cachedPool.execute(runnable); ExecutorService fixedPool =
Executors.newFixedThreadPool(4); fixedPool.execute(runnable); w ʮλεΫʯΛผεϨουͰॲཧ͢ΔͨΊͷΈ w εϨουϓʔϧͷछྨΛࢦఆͰ͖Δ -
DDD@H 'VUVSF w 'VUVSFBQQMZ͞ΕͨॲཧΛඇಉظʹ࣮ߦ w ͍ͭͲͷΑ͏ʹඇಉظʹ࣮ߦ͢Δ͔&YFDVUJPO$POUFYU࣍ୈ import scala.concurrent.Future // ForkJoinPoolɺσϑΥϧτͰίΞͷฒྻͰॲཧ
import scala.concurrent.ExecutionContext. Implicits.global val result: Future[User] = Future { userRepository.fetch(userId) } result.map(user => user.id) -
DDD@H &YFDVUJPO$POUFYUͲ͏ड͚औΔʁ w 'VUVSFBQQMZʹҾϒϩοΫ͕ͭ w ͭͷҾϒϩοΫͰ&YFDVUJPO$POUFYUΛड͚औΔ object Future { ...
def apply[T](body: =>T)(implicit executor: ExecutionContext) } Future { userRepository.fetch(user) } Future.apply( userRepository.fetch(user) ) -
DDD@H JNQMJDJU8IBUͱ)PXͷ w )PX͕ڊେʹͳΔͱɺίʔυͷຊདྷͷҙਤ͕ຒΕͯ͠·͏ w JNQMJDJUͷେ͖ͳϞνϕʔγϣϯ)PXͷӅณ w 8IBUͱ)PXΛ͢Δ͜ͱͰɺతΛతʹࣔ͢͜ͱ͕Ͱ͖Δ val values:
Seq[(String, Option[Int])] val sorted = sort(values)( tuple2Comparator( stringComparator, optionComparator(intComparator))) val sorted = sort(values) // implicit -
DDD@H $MPKVSFͷฒߦॲཧؔ w $MPKVSFͷฒߦॲཧؔͨ͘͞Μ͋Δ w HP w UISFBE w TFOE
w TFOEPGG w GVUVSF w FUD -
DDD@H $MPKVSFͷฒߦॲཧؔ εϨουϓʔϧ εϨου TFOE 'JYFE5ISFBE1PPM ίΞ TFOEPGG $BDIFE5ISFBE1PPM
੍ݶͳ͠ GVUVSFGVUVSFDBMM QNBQQDBMMT $BDIFE5ISFBE1PPM ੍ݶͳ͠ HP 'JYFE5ISFBE1PPM ίΞ UISFBE UISFBEDBMM $BDIFE5ISFBE1PPM ੍ݶͳ͠ SFEVDFST 'PSL+PJO1PPM ࣗಈ੍ޚ IUUQUZBOPTIFMpODDPNQPTUDMPKVSFDPODVSSFOU -
DDD@H *0όϯυ$16όϯυ w $MPKVSFͷฒߦॲཧؔɺ*0$16όϯυͷछྨʹେผ *0όϯυͳॲཧɺ࣮ߦதͷॲཧ͕$16ΑΓ*0ॲཧʹ ڧ͘ґଘ͠·͢ɻ%#ΞΫηεͱ͔ϦϞʔτ௨৴ͱ͔Ͱ͢Ͷɻ ผεϨουͰ͜ͷॲཧΛ࣮ߦͨ͠߹ɺεϨουେ෦Λɺ *0ॲཧͪঢ়ଶͰա͝͠·͢ɻ $16όϯυͳॲཧɺ్தʹ*0ͪͷΑ͏ͳʮػʯ͕ ൃੜͤͣɺ$16ΛͿΜճ͠ଓ͚ΔΑ͏ͳॲཧͰ͢ɻ
શσʔλ͕ϝϞϦʹࡌ͍ͬͯͯɺ$16͕ϑϧՔಇͰͦΕΒΛ ॲཧ͢ΔΑ͏ͳέʔεͰ͢ɻ IUUQUZBOPTIFMpODDPNQPTUDMPKVSFDPODVSSFOU -
DDD@H $MPKVSFͷฒߦॲཧؔ εϨουϓʔϧ εϨου TFOE 'JYFE5ISFBE1PPM ίΞ TFOEPGG $BDIFE5ISFBE1PPM
੍ݶͳ͠ GVUVSFGVUVSFDBMM QNBQQDBMMT $BDIFE5ISFBE1PPM ੍ݶͳ͠ HP 'JYFE5ISFBE1PPM ίΞ UISFBE UISFBEDBMM $BDIFE5ISFBE1PPM ੍ݶͳ͠ SFEVDFST 'PSL+PJO1PPM ࣗಈ੍ޚ IUUQUZBOPTIFMpODDPNQPTUDMPKVSFDPODVSSFOU -
DDD@H $MPKVSFͷฒߦॲཧؔ εϨουϓʔϧ εϨου TFOE 'JYFE5ISFBE1PPM ίΞ TFOEPGG $BDIFE5ISFBE1PPM
੍ݶͳ͠ GVUVSFGVUVSFDBMM QNBQQDBMMT $BDIFE5ISFBE1PPM ੍ݶͳ͠ HP 'JYFE5ISFBE1PPM ίΞ UISFBE UISFBEDBMM $BDIFE5ISFBE1PPM ੍ݶͳ͠ SFEVDFST 'PSL+PJO1PPM ࣗಈ੍ޚ IUUQUZBOPTIFMpODDPNQPTUDMPKVSFDPODVSSFOU -
DDD@H UISFBE (thread (with-open [s socket in (.getInputStream s) out
(.getOutputStream s)] (-> (request-parser/from-input-stream in) (request-handler/handle-request) (response-writer/write out)))) DPSFDMKʢൈਮʣ -
DDD@H จࣈྻ݁߹ w ϨεϙϯεʢΦϒδΣΫτϚοϓʣ͔Β )551ϨεϙϯεϔομΛΈཱ͍ͯͨ public class Response { public
final Status status; public final String contentType; public final int contentLength; public final byte[] body; } -
DDD@H 4USJOH#VJMEFS String response = "HTTP/1.1 " + status.statusCode +
CRLF + "Server: SimpleJavaHttpServer" + CRLF + "Content-Type: " + contentType + CRLF + "Content-Length: " + String.valueOf(contentLength) + CRLF + "Connection: Close" + CRLF + CRLF; 3FTQPOTFKBWBʢൈਮʣ ࠷దԽ͞ΕΔ͔ࣗ৴͕ͳ͚ΕKBWBQD -
DDD@H 4USJOH*OUFSQPMBUJPO w จࣈྻϦςϥϧͷલʹTΛ͚ΔͱɺมΛల։Ͱ͖Δ val price = 1000 s"Price is
$price" // => Price is 1000 -
DDD@H 5SJQMF2VPUF "Hello triple quote!\nHello stripMargin!" """Hello triple quote! Hello
stripMargin!""" """|Hello triple quote! |Hello stripMargin!""".stripMargin w վߦΛؚΉจࣈྻΛຒΊࠐΉʹɺΛ͏ w ΠϯσϯτΛଗ͑ΔʹΠϯσϯτจࣈ c ͱTUSJQ.BSHJOΛ͏ -
DDD@H 4USJOH*OUFSQPMBUJPOͱ5SJQMF2VPUF val response = s"""HTTP/1.1 ${status.value} |Date: ${rfc1123Formatter.format(now)} |Server:
SimpleScalaHttpServer |Content-Type: $contentType |Content-Length: ${body.length.toString} |Connection: Close | |""".stripMargin 3FTQPOTFTDBMBʢൈਮʣ -
DDD@H w $MPKVSFͰจࣈྻͷ݁߹ Ͱͳ͘TUS w ՄมҾɺ4ࣜͳΒͰ TUS (+ "hoge" "fuga")
ClassCastException java.lang.String cannot be cast to java.lang.Number clojure.lang.Numbers.add (Numbers.java:128) (str "hoge" "fuga" "piyo") // => hogefugapiyo -
DDD@H TUS (let [header (str "HTTP/1.1" sp status sp reason-phrase
crlf "Content-Length:" (count body) crlf "Content-Type:" content-type crlf "Connection: Close" crlf crlf)] ...) SFTQPOTF@XSJUFSDMKʢൈਮʣ -
DDD@H )551αʔόʔΛ࡞ͬͯݴޠΛֶ΅͏ w %#ը໘Λ༻ҙ͢Δखؒͳ͘ɺखܰʹ։ൃ͕࢝ΊΒΕΔ w 8FC։ൃʹඞཁͳཁૉʹͦͦ͜͜Ұ௨Γ৮ΕΒΕΔ w จࣈྻૢ࡞ɺೖग़ྗɺฒྻॲཧͳͲ w ࠷ॳ͘͠ߟ͑͗͢ͳ͍ɻ
ॳΊ͔Βߟ͑ํΛࠜຊతʹม͑Δඞཁͳ͍ w طʹ͍ͬͯΔ͜ͱͱEJGGΛͱͬͯΈΑ͏ w ͦͷݴޠͷੈք؍ΛΔख͕͔Γ͕ͦͷEJGGʹ͋Δ͔