├── .github └── ISSUE_TEMPLATE │ ├── improvement.md │ └── question.md ├── .gitignore ├── LICENSE ├── README.md ├── articles ├── 11-29-delaing-update.md ├── concludion--.md ├── publish-cookie.md ├── publish-daemonize.md ├── publish-delivery-static-html.md ├── publish-dynamic-response-generation.md ├── publish-improve-to-minimal-web-server.md ├── publish-modulize.md ├── publish-post-parameters.md ├── publish-response-request-view.md ├── publish-template-engine.md ├── publish-threading.md ├── publish-url-resolver.md ├── publish-what-is-http.md └── reason-why-you-should-take-paternity-leave.md ├── books └── introduction-to-web-application-with-python │ ├── config.yaml │ ├── cookie.md │ ├── cover.jpg │ ├── daemonize.md │ ├── delivery-static-html.md │ ├── dynamic-response-generation.md │ ├── epilogue.md │ ├── improve-to-minimal-web-server.md │ ├── introduction.md │ ├── making-silly-web-server-step1.md │ ├── making-silly-web-server-step2.md │ ├── making-silly-web-server-step3.md │ ├── making-silly-web-server-step4.md │ ├── making-silly-web-server.md │ ├── modulize.md │ ├── post-parameters.md │ ├── preface.md │ ├── response-request-view.md │ ├── template-engine.md │ ├── threading.md │ ├── url-resolver.md │ ├── what-is-http.md │ └── what-is-web-application.md ├── codes ├── chapter10 │ └── webserver.py ├── chapter11-2 │ ├── static │ │ └── index.html │ └── webserver.py ├── chapter11 │ ├── static │ │ └── index.html │ └── webserver.py ├── chapter12-2 │ ├── static │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ └── webserver.py ├── chapter12 │ ├── static │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ └── webserver.py ├── chapter13-2 │ ├── static │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── webserver.py │ └── workerthread.py ├── chapter13 │ ├── static │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ └── webserver.py ├── chapter14-2 │ ├── static │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── webserver.py │ └── workerthread.py ├── chapter14 │ ├── static │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── webserver.py │ └── workerthread.py ├── chapter15-2 │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── webserver.py │ └── workerthread.py ├── chapter15 │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── webserver.py │ └── workerthread.py ├── chapter16-2 │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── views.py │ ├── webserver.py │ └── workerthread.py ├── chapter16-3 │ ├── henango │ │ └── http │ │ │ ├── request.py │ │ │ └── response.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── views.py │ ├── webserver.py │ └── workerthread.py ├── chapter16-4 │ ├── henango │ │ └── http │ │ │ ├── request.py │ │ │ └── response.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── urls.py │ ├── views.py │ ├── webserver.py │ └── workerthread.py ├── chapter16 │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── views.py │ ├── webserver.py │ └── workerthread.py ├── chapter17-2 │ ├── henango │ │ ├── http │ │ │ ├── request.py │ │ │ └── response.py │ │ └── server │ │ │ ├── server.py │ │ │ └── worker.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── urls.py │ └── views.py ├── chapter17-3 │ ├── henango │ │ ├── http │ │ │ ├── request.py │ │ │ └── response.py │ │ └── server │ │ │ ├── server.py │ │ │ └── worker.py │ ├── settings.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── urls.py │ └── views.py ├── chapter17 │ ├── henango │ │ └── http │ │ │ ├── request.py │ │ │ └── response.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── urls.py │ ├── views.py │ ├── webserver.py │ └── workerthread.py ├── chapter18-2 │ ├── henango │ │ ├── http │ │ │ ├── request.py │ │ │ └── response.py │ │ ├── server │ │ │ ├── server.py │ │ │ └── worker.py │ │ └── urls │ │ │ └── pattern.py │ ├── settings.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── urls.py │ └── views.py ├── chapter18-3 │ ├── henango │ │ ├── http │ │ │ ├── request.py │ │ │ └── response.py │ │ ├── server │ │ │ ├── server.py │ │ │ └── worker.py │ │ └── urls │ │ │ ├── pattern.py │ │ │ └── resolver.py │ ├── settings.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── urls.py │ └── views.py ├── chapter18-4 │ ├── henango │ │ ├── http │ │ │ ├── request.py │ │ │ └── response.py │ │ ├── server │ │ │ ├── server.py │ │ │ └── worker.py │ │ ├── urls │ │ │ ├── pattern.py │ │ │ └── resolver.py │ │ └── views │ │ │ └── static.py │ ├── settings.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── urls.py │ └── views.py ├── chapter18 │ ├── henango │ │ ├── http │ │ │ ├── request.py │ │ │ └── response.py │ │ └── server │ │ │ ├── server.py │ │ │ └── worker.py │ ├── settings.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── urls.py │ └── views.py ├── chapter19-2 │ ├── henango │ │ ├── http │ │ │ ├── request.py │ │ │ └── response.py │ │ ├── server │ │ │ ├── server.py │ │ │ └── worker.py │ │ ├── template │ │ │ └── renderer.py │ │ ├── urls │ │ │ ├── pattern.py │ │ │ └── resolver.py │ │ └── views │ │ │ └── static.py │ ├── settings.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── templates │ │ └── now.html │ ├── urls.py │ └── views.py ├── chapter19-3 │ ├── henango │ │ ├── http │ │ │ ├── request.py │ │ │ └── response.py │ │ ├── server │ │ │ ├── server.py │ │ │ └── worker.py │ │ ├── template │ │ │ └── renderer.py │ │ ├── urls │ │ │ ├── pattern.py │ │ │ └── resolver.py │ │ └── views │ │ │ └── static.py │ ├── settings.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── templates │ │ └── now.html │ ├── urls.py │ └── views.py ├── chapter19-4 │ ├── henango │ │ ├── http │ │ │ ├── request.py │ │ │ └── response.py │ │ ├── server │ │ │ ├── server.py │ │ │ └── worker.py │ │ ├── template │ │ │ └── renderer.py │ │ ├── urls │ │ │ ├── pattern.py │ │ │ └── resolver.py │ │ └── views │ │ │ └── static.py │ ├── settings.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── templates │ │ └── now.html │ ├── urls.py │ └── views.py ├── chapter19-5 │ ├── henango │ │ ├── http │ │ │ ├── request.py │ │ │ └── response.py │ │ ├── server │ │ │ ├── server.py │ │ │ └── worker.py │ │ ├── template │ │ │ └── renderer.py │ │ ├── urls │ │ │ ├── pattern.py │ │ │ └── resolver.py │ │ └── views │ │ │ └── static.py │ ├── settings.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── templates │ │ └── now.html │ ├── urls.py │ └── views.py ├── chapter19-6 │ ├── henango │ │ ├── http │ │ │ ├── request.py │ │ │ └── response.py │ │ ├── server │ │ │ ├── server.py │ │ │ └── worker.py │ │ ├── template │ │ │ └── renderer.py │ │ ├── urls │ │ │ ├── pattern.py │ │ │ └── resolver.py │ │ └── views │ │ │ └── static.py │ ├── settings.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── templates │ │ ├── now.html │ │ ├── parameters.html │ │ ├── show_request.html │ │ └── user_profile.html │ ├── urls.py │ └── views.py ├── chapter19 │ ├── henango │ │ ├── http │ │ │ ├── request.py │ │ │ └── response.py │ │ ├── server │ │ │ ├── server.py │ │ │ └── worker.py │ │ ├── urls │ │ │ ├── pattern.py │ │ │ └── resolver.py │ │ └── views │ │ │ └── static.py │ ├── settings.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── templates │ │ └── now.html │ ├── urls.py │ └── views.py ├── chapter20-2 │ ├── henango │ │ ├── http │ │ │ ├── request.py │ │ │ └── response.py │ │ ├── server │ │ │ ├── server.py │ │ │ └── worker.py │ │ ├── template │ │ │ └── renderer.py │ │ ├── urls │ │ │ ├── pattern.py │ │ │ └── resolver.py │ │ └── views │ │ │ └── static.py │ ├── settings.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── templates │ │ ├── login.html │ │ ├── now.html │ │ ├── parameters.html │ │ ├── show_request.html │ │ ├── user_profile.html │ │ └── welcome.html │ ├── urls.py │ └── views.py ├── chapter20-3 │ ├── henango │ │ ├── http │ │ │ ├── request.py │ │ │ └── response.py │ │ ├── server │ │ │ ├── server.py │ │ │ └── worker.py │ │ ├── template │ │ │ └── renderer.py │ │ ├── urls │ │ │ ├── pattern.py │ │ │ └── resolver.py │ │ └── views │ │ │ └── static.py │ ├── settings.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── templates │ │ ├── login.html │ │ ├── now.html │ │ ├── parameters.html │ │ ├── show_request.html │ │ ├── user_profile.html │ │ └── welcome.html │ ├── urls.py │ └── views.py ├── chapter20-4 │ ├── henango │ │ ├── http │ │ │ ├── cookie.py │ │ │ ├── request.py │ │ │ └── response.py │ │ ├── server │ │ │ ├── server.py │ │ │ └── worker.py │ │ ├── template │ │ │ └── renderer.py │ │ ├── urls │ │ │ ├── pattern.py │ │ │ └── resolver.py │ │ └── views │ │ │ └── static.py │ ├── settings.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── templates │ │ ├── login.html │ │ ├── now.html │ │ ├── parameters.html │ │ ├── show_request.html │ │ ├── user_profile.html │ │ └── welcome.html │ ├── urls.py │ └── views.py ├── chapter20 │ ├── henango │ │ ├── http │ │ │ ├── request.py │ │ │ └── response.py │ │ ├── server │ │ │ ├── server.py │ │ │ └── worker.py │ │ ├── template │ │ │ └── renderer.py │ │ ├── urls │ │ │ ├── pattern.py │ │ │ └── resolver.py │ │ └── views │ │ │ └── static.py │ ├── settings.py │ ├── start.py │ ├── static │ │ ├── form.html │ │ ├── index.css │ │ ├── index.html │ │ └── logo.png │ ├── templates │ │ ├── now.html │ │ ├── parameters.html │ │ ├── show_request.html │ │ └── user_profile.html │ ├── urls.py │ └── views.py ├── chapter6 │ └── tcpserver.py ├── chapter7 │ ├── client_send.txt │ └── tcpclient.py └── chapter8 │ ├── server_send.txt │ └── tcpserver.py ├── package-lock.json ├── package.json └── pyproject.toml /.github/ISSUE_TEMPLATE/improvement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 改善点について 3 | about: 誤植や、ソースコードが間違っている場合など、内容をより良くできる箇所があれば教えて下さい。 4 | title: ex) Chapter7に誤植がある、Chapter3のソースコードが適切ではない 5 | labels: enhancement 6 | assignees: bigen1925 7 | 8 | --- 9 | 10 | # どの箇所をもっと良く出来ますか? 11 | 12 | < 誤植箇所や、ソースコードの改善点などについて、具体的な箇所を教えて下さい。 > 13 | 14 | 15 | 16 | 17 | # どのように良く出来ますか? 18 | 19 | <どのように修正すると、より良くなるか教えて下さい> 20 | 21 | 22 | 23 | 24 | # 変更すると、なぜ良くなりますか? 25 | 26 | <変更したほうが良いと思う理由を教えて下さい> 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 質 問 3 | about: 内容について理解できない箇所や、ソースコードが動かず先に進めない場合など、質問したい場合はこちらを使ってください。 4 | title: ex) Content-Typeについて教えて下さい、Chapter6のソースコード1でエラーが発生します 5 | labels: question 6 | assignees: bigen1925 7 | 8 | --- 9 | 10 | # 何について聞きたいですか? 11 | 12 | <ここに質問の内容を書いてください。> 13 | 14 | <該当の箇所のリンクや本文、あなたのソースコードやエラーメッセージなど、サンプルをできるだけ多く引用してください> 15 | 16 | 17 | # あなたの環境を教えて下さい 18 | 19 | | | | 20 | |--------|---------------------------------------| 21 | | OS | | 22 | | Python | <3.5, 3.8, など> | 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Jet Brains' editor configuration 2 | .idea 3 | 4 | # python cache 5 | __pycache__ 6 | 7 | # node packages 8 | node_modules 9 | 10 | # dynamic resources of application 11 | server_recv.txt 12 | client_recv.txt 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 bigen1925 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ここはなに? 2 | 「[伸び悩んでいる3年目Webエンジニアのための、Python Webアプリケーション自作入門](https://zenn.dev/bigen1925/books/introduction-to-web-application-with-python)」のソースコードリポジトリです。 3 | 4 | # 質問したい方 5 | 本書の内容で分からないことがあったり、エラーで困っている方は、[こちら](https://github.com/bigen1925/introduction-to-web-application-with-python/issues/new/choose)からISSUEを作成してみてください。:wink: 6 | 7 | 8 | # 改善点を教えてくれる方 9 | 誤植や、不適切なソースコードなど、本書の内容をより良くできる点を教えていただける方も、[こちら](https://github.com/bigen1925/introduction-to-web-application-with-python/issues/new/choose)からISSUEを作成して教えてください。:kissing_heart: 10 | 11 | 12 | -------------------------------------------------------------------------------- /articles/11-29-delaing-update.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "オンラインブックの更新頻度が遅くなります" 3 | emoji: "🚶" 4 | type: "tech" 5 | topics: [idea] 6 | published: true 7 | --- 8 | 9 | 10 | [伸び悩んでいる3年目Webエンジニアのための、Python Webアプリケーション自作入門](https://zenn.dev/bigen1925/books/introduction-to-web-application-with-python) をご愛読いただいている皆さん、ありがとうございます ;-) 11 | 12月以降は更新頻度を落とさざるを得ないため、皆様にお知らせします。 12 | 13 | --- 14 | 15 | 私事ではありますが、11月末で現職を退職し、12月から新しい会社での業務が始まります。 16 | 11月中は仕事の切れ間ということもありかなりの時間的余裕があったため、精力的にオンラインブックを更新してきました。 17 | 18 | しかし、12月からはこれまでと同じペースで更新、というわけにはいかないと思われます。 19 | それでもできれば **週に1回は更新したい** と思っておりますので、変わらずのご愛読をいただけますと幸いです。 20 | 21 | --- 22 | 23 | さきほど17章を更新したところですが、オンラインブック全体のボリュームもかなりなものになってきていまして、Zennの中でも最も長いBookの一つになってきました。 24 | オンラインとはいえ、書籍というような大きな単位でアウトプットする経験は初めてですので、構成作りやソースコードの提供の仕方などもかなり頭を悩ませ、ここまで辿り着くのも当初考えていたより何倍も大変でした。 25 | 26 | しかし、皆さんのLike!や、誤植指摘などのコントリビューション、アンケート回答などのフィードバックが間違いなく大きな支えとなりここまで来れたと思います。 27 | 28 | 現時点の構想では、まだ全体でみると折返し地点まで来たかどうか、という気持ちではありますが、なんとか今後も続けて完走できたら良いなと思っています。 29 | 30 | 改めてフィードバックを寄せていただいた読者の方々に深くお礼申し上げます。 31 | 32 | --- 33 | 最後に、皆さんからのフィードバックは引き続き絶賛募集中です。 34 | 35 | 本書の改善要望があれば、[Github](https://github.com/bigen1925/introduction-to-web-application-with-python) までIssueやPRをお寄せいただくか、単に感想等があれば[Googleフォーム](https://docs.google.com/forms/d/1qTTNPbyPyAAMYALth5uDqgsDGmlCe-BXLYYDLk0QKfw/edit?usp=drive_web) のアンケートでも受け付けております。 36 | 37 | フィードバックが多ければ多いほど本書の執筆は継続します。 38 | Googleフォームのアンケートは1分ほどで回答できますので、まだ回答されていない方は是非ご協力ください。 39 | 40 | また、新たな読者が増えていくことも本書の執筆継続を大きく支えてくれます。 41 | Twitter等で「これ良かったよ」と一言ご共有いただければ、それだけで筆者は泣いて喜びます。 42 | 43 | --- 44 | 45 | それでは皆さん、今後も是非本書の執筆をご応援ください。 46 | 47 | 少し早いですが、よいお年を! ;-) -------------------------------------------------------------------------------- /articles/concludion--.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "[完結しました] 「伸び悩んでいる3年目Webエンジニアのための、Python Webアプリケーション自作入門」" 3 | emoji: "🚶" 4 | type: "tech" 5 | topics: [python, web, http] 6 | published: true 7 | --- 8 | 9 | # 本を完結させました 10 | 11 | [伸び悩んでいる3年目Webエンジニアのための、Python Webアプリケーション自作入門](https://zenn.dev/bigen1925/books/introduction-to-web-application-with-python) 12 | 13 | を完結させました。 14 | 15 | もう少し多くの内容を盛り込もうかと考えていたのですが、これ以上はどうしても内容が複雑になってしまい、入門書の域を超えてきそう(というかとっつきにくい本になってしまう)なので男らしく諦めて割愛することにしました。 16 | 17 | # ふりかえり 18 | 終わってみると20万字という大量の文章となり、我ながらよく書いたものだと思います。 19 | 執筆中にアンケートよりフィードバックをくれた読者の皆様、サポートをいただいたcatnose様の支えあってこそだと改めて思います。 20 | 21 | 僕はこのプログラミングというゲームをとっても楽しんでいて、先人たちのプログラムや技術書に感謝する日々であり、自分もこの楽しみを誰かに分け与える側に回らねばならないと思っていたのが一つ形にできたかなという充足感があります。 22 | 23 | 一方で、わかってはいましたが、やはり技術書をまとめるというのは膨大な時間と労力がかかるのだという実感も得られました。 24 | 25 | 最近は書籍の執筆以外にもやりたいことが多く、いったん情報発信はお休みしようかと思いますが、いつかまた誰かに恩返しできたらいいなと思います。 26 | 27 | 28 | それでは、またどこかで ;D -------------------------------------------------------------------------------- /books/introduction-to-web-application-with-python/config.yaml: -------------------------------------------------------------------------------- 1 | title: "伸び悩んでいる3年目Webエンジニアのための、Python Webアプリケーション自作入門" 2 | summary: | 3 | 本書は、Webフレームワークを使ってお作法に則ってWebサービスを作ることができるようになってきたはいいものの、周囲の先輩エンジニアほど自分のサービスを深く理解できているわけではなく、かといって業務では同じような作業の繰り返しで学びが深まっている気がせず、スキルが頭打ちになっているのではないかと悩む、3年目Webエンジニアに捧げる本です。 4 | 5 | Webサーバーをフレームワークやライブラリを極力使わずに自作することでWebアプリケーションの動作を深く理解し、より高度なWebサービス開発ができるようになることをゴールとしています。 6 | なお、言語はPythonを使用します。 7 | 8 | 本書は当面無料で公開する予定ですが、予告なく有料に変更する可能性があります。 9 | 勉強になったという方は、ぜひ無料のうちにTwitterやFacebookなどで友人に紹介してあげてください ;) 10 | 11 | また、本書では今後の情報発信の参考のために読者の皆様のご意見を募集しています 12 | 今後の新しい書籍の執筆や、単発記事の執筆などをご期待されるかたは、是非下記アンケートフォームよりご回答ください。(所要時間1分) 13 | https://forms.gle/TXsfdPspD91Lmhzu5 14 | 15 | === 本書に関する質問や誤植連絡、ソースコードはこちらから === 16 | https://github.com/bigen1925/introduction-to-web-application-with-python 17 | 18 | topics: ["python", "web", "socket", "http", "tcp"] # トピック(5つまで) 19 | published: true 20 | price: 0 21 | chapters: 22 | - preface.md 23 | - introduction.md 24 | - what-is-web-application.md 25 | - making-silly-web-server.md 26 | - making-silly-web-server-step1.md 27 | - making-silly-web-server-step2.md 28 | - making-silly-web-server-step3.md 29 | - making-silly-web-server-step4.md 30 | - what-is-http.md 31 | - improve-to-minimal-web-server.md 32 | - delivery-static-html.md 33 | - daemonize.md 34 | - threading.md 35 | - dynamic-response-generation.md 36 | - post-parameters.md 37 | - response-request-view.md 38 | - modulize.md 39 | - url-resolver.md 40 | - template-engine.md 41 | - cookie.md 42 | - epilogue -------------------------------------------------------------------------------- /books/introduction-to-web-application-with-python/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/books/introduction-to-web-application-with-python/cover.jpg -------------------------------------------------------------------------------- /books/introduction-to-web-application-with-python/epilogue.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "おわりに" 3 | --- 4 | 5 | さて、本書はここまでとなります。 6 | ここまでのまとまった量の技術ナレッジをアウトプットするのは初めての経験であり、拙い文章にお付き合いいただいた読者の皆様には感謝の限りです。 7 | 8 | ご自身でソースコードを書かれながらここまでたどり着いた方は、 **Webサーバー** や **Webフレームワーク** という魔法の道具の中身がかなり理解できたのではないでしょうか。 9 | 10 | ---- 11 | 12 | 現在世間一般でよく使われているフレームワークは、本書で紹介した 13 | 14 | - HTTPサーバーとしての基本的な機能 15 | - ルーティング 16 | - テンプレートエンジン 17 | - Cookie 18 | 19 | などの超基本的な機能の他にも、これらの機能を組み合わせてさらに発展的に使いやすくした 20 | 21 | - ミドルウェア 22 | - Session 23 | - ファイルアップロード 24 | 25 | といった機能や、あるいはデータベースとの連携まで組み込まれたものも多く存在します。 26 | 実務として扱うにはこれらの機能についても理解が必須となってくるでしょう。 27 | 28 | 当初の計画ではこれらも本書で扱う予定としていたのですが、「入門」と呼ぶには発展的で複雑すぎる内容となってしまうため、割愛することにしました。 29 | とはいえ、これらの内容は本書で紹介した基本的な機能を組み合わせて進化させたものであり、本書の内容を十分に理解されていればご自身で勉強されても格段に理解しやすくなっていると思います。 30 | 31 | ---- 32 | 33 | 最後に、本書の執筆のきっかけとなった前橋和弥先生にこの場を借りて改めてお礼申し上げます。 34 | 35 | また、本書の執筆中にフィードバックをお寄せいただいた皆様にも感謝しております。 36 | 37 | 本書が一人でも多くのエンジニアの助けとなり、プログラミングというゲームを一緒に楽しんでくれる仲間が増えていると良いなと思います。 38 | 39 | ---- 40 | 41 | 本書では、今後の参考のために皆様からのアンケートを募集しております。 42 | 1分程度で回答できますので、ぜひご意見をいただけると今後の情報発信の励みとなりますので、ご協力ください。 43 | 44 | また、本書で割愛した発展的な内容について、続編という形で執筆するかどうかは検討中です。 45 | 46 | 読者の皆様のご希望が多ければいつか取り組んでみようかなとも思いますので、ご希望される方は是非以下のアンケートよりご意見をお寄せください。 47 | 48 | https://forms.gle/TXsfdPspD91Lmhzu5 49 | 50 | 51 | -------------------------------------------------------------------------------- /codes/chapter10/webserver.py: -------------------------------------------------------------------------------- 1 | import socket 2 | from datetime import datetime 3 | 4 | 5 | class WebServer: 6 | """ 7 | Webサーバーを表すクラス 8 | """ 9 | 10 | def serve(self): 11 | """ 12 | サーバーを起動する 13 | """ 14 | 15 | print("=== サーバーを起動します ===") 16 | 17 | try: 18 | # socketを生成 19 | server_socket = socket.socket() 20 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 21 | 22 | # socketをlocalhostのポート8080番に割り当てる 23 | server_socket.bind(("localhost", 8080)) 24 | server_socket.listen(10) 25 | 26 | # 外部からの接続を待ち、接続があったらコネクションを確立する 27 | print("=== クライアントからの接続を待ちます ===") 28 | (client_socket, address) = server_socket.accept() 29 | print(f"=== クライアントとの接続が完了しました remote_address: {address} ===") 30 | 31 | # クライアントから送られてきたデータを取得する 32 | request = client_socket.recv(4096) 33 | 34 | # クライアントから送られてきたデータをファイルに書き出す 35 | with open("server_recv.txt", "wb") as f: 36 | f.write(request) 37 | 38 | # レスポンスボディを生成 39 | response_body = "

It works!

" 40 | 41 | # レスポンスラインを生成 42 | response_line = "HTTP/1.1 200 OK\r\n" 43 | # レスポンスヘッダーを生成 44 | response_header = "" 45 | response_header += f"Date: {datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')}\r\n" 46 | response_header += "Host: HenaServer/0.1\r\n" 47 | response_header += f"Content-Length: {len(response_body.encode())}\r\n" 48 | response_header += "Connection: Close\r\n" 49 | response_header += "Content-Type: text/html\r\n" 50 | 51 | # ヘッダーとボディを空行でくっつけた上でbytesに変換し、レスポンス全体を生成する 52 | response = (response_line + response_header + "\r\n" + response_body).encode() 53 | 54 | # クライアントへレスポンスを送信する 55 | client_socket.send(response) 56 | 57 | # 通信を終了させる 58 | client_socket.close() 59 | 60 | finally: 61 | print("=== サーバーを停止します。 ===") 62 | 63 | 64 | if __name__ == "__main__": 65 | server = WebServer() 66 | server.serve() 67 | -------------------------------------------------------------------------------- /codes/chapter11-2/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 |

Welcome to HenaServer!

9 | 10 | -------------------------------------------------------------------------------- /codes/chapter11/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 |

Welcome to HenaServer!

9 | 10 | -------------------------------------------------------------------------------- /codes/chapter12-2/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter12-2/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter12-2/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter12-2/static/logo.png -------------------------------------------------------------------------------- /codes/chapter12/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter12/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter12/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter12/static/logo.png -------------------------------------------------------------------------------- /codes/chapter13-2/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter13-2/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter13-2/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter13-2/static/logo.png -------------------------------------------------------------------------------- /codes/chapter13-2/webserver.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from workerthread import WorkerThread 4 | 5 | 6 | class WebServer: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = WorkerThread(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | 50 | 51 | if __name__ == "__main__": 52 | server = WebServer() 53 | server.serve() 54 | -------------------------------------------------------------------------------- /codes/chapter13/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter13/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter13/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter13/static/logo.png -------------------------------------------------------------------------------- /codes/chapter14-2/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter14-2/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter14-2/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter14-2/static/logo.png -------------------------------------------------------------------------------- /codes/chapter14-2/webserver.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from workerthread import WorkerThread 4 | 5 | 6 | class WebServer: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = WorkerThread(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | 50 | 51 | if __name__ == "__main__": 52 | server = WebServer() 53 | server.serve() 54 | -------------------------------------------------------------------------------- /codes/chapter14/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter14/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter14/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter14/static/logo.png -------------------------------------------------------------------------------- /codes/chapter14/webserver.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from workerthread import WorkerThread 4 | 5 | 6 | class WebServer: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = WorkerThread(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | 50 | 51 | if __name__ == "__main__": 52 | server = WebServer() 53 | server.serve() 54 | -------------------------------------------------------------------------------- /codes/chapter15-2/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter15-2/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter15-2/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter15-2/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter15-2/static/logo.png -------------------------------------------------------------------------------- /codes/chapter15-2/webserver.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from workerthread import WorkerThread 4 | 5 | 6 | class WebServer: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = WorkerThread(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | 50 | 51 | if __name__ == "__main__": 52 | server = WebServer() 53 | server.serve() 54 | -------------------------------------------------------------------------------- /codes/chapter15/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter15/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter15/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter15/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter15/static/logo.png -------------------------------------------------------------------------------- /codes/chapter15/webserver.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from workerthread import WorkerThread 4 | 5 | 6 | class WebServer: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = WorkerThread(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | 50 | 51 | if __name__ == "__main__": 52 | server = WebServer() 53 | server.serve() 54 | -------------------------------------------------------------------------------- /codes/chapter16-2/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter16-2/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter16-2/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter16-2/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter16-2/static/logo.png -------------------------------------------------------------------------------- /codes/chapter16-2/webserver.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from workerthread import WorkerThread 4 | 5 | 6 | class WebServer: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = WorkerThread(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | 50 | 51 | if __name__ == "__main__": 52 | server = WebServer() 53 | server.serve() 54 | -------------------------------------------------------------------------------- /codes/chapter16-3/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | 8 | def __init__( 9 | self, path: str = "", method: str = "", http_version: str = "", headers: dict = None, body: bytes = b"" 10 | ): 11 | if headers is None: 12 | headers = {} 13 | 14 | self.path = path 15 | self.method = method 16 | self.http_version = http_version 17 | self.headers = headers 18 | self.body = body 19 | -------------------------------------------------------------------------------- /codes/chapter16-3/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | content_type: Optional[str] 7 | body: bytes 8 | 9 | def __init__(self, status_code: int, content_type: str = None, body: bytes = b""): 10 | self.status_code = status_code 11 | self.content_type = content_type 12 | self.body = body 13 | -------------------------------------------------------------------------------- /codes/chapter16-3/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter16-3/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter16-3/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter16-3/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter16-3/static/logo.png -------------------------------------------------------------------------------- /codes/chapter16-3/views.py: -------------------------------------------------------------------------------- 1 | import textwrap 2 | import urllib.parse 3 | from datetime import datetime 4 | from pprint import pformat 5 | 6 | from henango.http.request import HTTPRequest 7 | from henango.http.response import HTTPResponse 8 | 9 | 10 | def now(request: HTTPRequest) -> HTTPResponse: 11 | """ 12 | 現在時刻を表示するHTMLを生成する 13 | """ 14 | html = f"""\ 15 | 16 | 17 |

Now: {datetime.now()}

18 | 19 | 20 | """ 21 | body = textwrap.dedent(html).encode() 22 | content_type = "text/html; charset=UTF-8" 23 | 24 | return HTTPResponse(body=body, content_type=content_type, status_code=200) 25 | 26 | 27 | def show_request(request: HTTPRequest) -> HTTPResponse: 28 | """ 29 | HTTPリクエストの内容を表示するHTMLを生成する 30 | """ 31 | html = f"""\ 32 | 33 | 34 |

Request Line:

35 |

36 | {request.method} {request.path} {request.http_version} 37 |

38 |

Headers:

39 |
{pformat(request.headers)}
40 |

Body:

41 |
{request.body.decode("utf-8", "ignore")}
42 | 43 | 44 | 45 | """ 46 | body = textwrap.dedent(html).encode() 47 | content_type = "text/html; charset=UTF-8" 48 | 49 | return HTTPResponse(body=body, content_type=content_type, status_code=200) 50 | 51 | 52 | def parameters(request: HTTPRequest) -> HTTPResponse: 53 | """ 54 | POSTパラメータを表示するHTMLを表示する 55 | """ 56 | 57 | # GETリクエストの場合は、405を返す 58 | if request.method == "GET": 59 | body = b"

405 Method Not Allowed

" 60 | content_type = "text/html; charset=UTF-8" 61 | status_code = 405 62 | 63 | elif request.method == "POST": 64 | post_params = urllib.parse.parse_qs(request.body.decode()) 65 | html = f"""\ 66 | 67 | 68 |

Parameters:

69 |
{pformat(post_params)}
70 | 71 | 72 | """ 73 | body = textwrap.dedent(html).encode() 74 | content_type = "text/html; charset=UTF-8" 75 | status_code = 200 76 | 77 | return HTTPResponse(body=body, content_type=content_type, status_code=status_code) 78 | -------------------------------------------------------------------------------- /codes/chapter16-3/webserver.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from workerthread import WorkerThread 4 | 5 | 6 | class WebServer: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = WorkerThread(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | 50 | 51 | if __name__ == "__main__": 52 | server = WebServer() 53 | server.serve() 54 | -------------------------------------------------------------------------------- /codes/chapter16-4/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | 8 | def __init__( 9 | self, path: str = "", method: str = "", http_version: str = "", headers: dict = None, body: bytes = b"" 10 | ): 11 | if headers is None: 12 | headers = {} 13 | 14 | self.path = path 15 | self.method = method 16 | self.http_version = http_version 17 | self.headers = headers 18 | self.body = body 19 | -------------------------------------------------------------------------------- /codes/chapter16-4/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | content_type: Optional[str] 7 | body: bytes 8 | 9 | def __init__(self, status_code: int, content_type: str = None, body: bytes = b""): 10 | self.status_code = status_code 11 | self.content_type = content_type 12 | self.body = body 13 | -------------------------------------------------------------------------------- /codes/chapter16-4/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter16-4/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter16-4/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter16-4/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter16-4/static/logo.png -------------------------------------------------------------------------------- /codes/chapter16-4/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | 3 | # pathとview関数の対応 4 | URL_VIEW = { 5 | "/now": views.now, 6 | "/show_request": views.show_request, 7 | "/parameters": views.parameters, 8 | } 9 | -------------------------------------------------------------------------------- /codes/chapter16-4/webserver.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from workerthread import WorkerThread 4 | 5 | 6 | class WebServer: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = WorkerThread(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | 50 | 51 | if __name__ == "__main__": 52 | server = WebServer() 53 | server.serve() 54 | -------------------------------------------------------------------------------- /codes/chapter16/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter16/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter16/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter16/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter16/static/logo.png -------------------------------------------------------------------------------- /codes/chapter16/webserver.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from workerthread import WorkerThread 4 | 5 | 6 | class WebServer: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = WorkerThread(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | 50 | 51 | if __name__ == "__main__": 52 | server = WebServer() 53 | server.serve() 54 | -------------------------------------------------------------------------------- /codes/chapter17-2/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | 8 | def __init__( 9 | self, path: str = "", method: str = "", http_version: str = "", headers: dict = None, body: bytes = b"" 10 | ): 11 | if headers is None: 12 | headers = {} 13 | 14 | self.path = path 15 | self.method = method 16 | self.http_version = http_version 17 | self.headers = headers 18 | self.body = body 19 | -------------------------------------------------------------------------------- /codes/chapter17-2/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | content_type: Optional[str] 7 | body: bytes 8 | 9 | def __init__(self, status_code: int, content_type: str = None, body: bytes = b""): 10 | self.status_code = status_code 11 | self.content_type = content_type 12 | self.body = body 13 | -------------------------------------------------------------------------------- /codes/chapter17-2/henango/server/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from henango.server.worker import Worker 4 | 5 | 6 | class Server: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = Worker(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter17-2/start.py: -------------------------------------------------------------------------------- 1 | from henango.server.server import Server 2 | 3 | if __name__ == "__main__": 4 | Server().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter17-2/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter17-2/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter17-2/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter17-2/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter17-2/static/logo.png -------------------------------------------------------------------------------- /codes/chapter17-2/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | 3 | # pathとview関数の対応 4 | URL_VIEW = { 5 | "/now": views.now, 6 | "/show_request": views.show_request, 7 | "/parameters": views.parameters, 8 | } 9 | -------------------------------------------------------------------------------- /codes/chapter17-3/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | 8 | def __init__( 9 | self, path: str = "", method: str = "", http_version: str = "", headers: dict = None, body: bytes = b"" 10 | ): 11 | if headers is None: 12 | headers = {} 13 | 14 | self.path = path 15 | self.method = method 16 | self.http_version = http_version 17 | self.headers = headers 18 | self.body = body 19 | -------------------------------------------------------------------------------- /codes/chapter17-3/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | content_type: Optional[str] 7 | body: bytes 8 | 9 | def __init__(self, status_code: int, content_type: str = None, body: bytes = b""): 10 | self.status_code = status_code 11 | self.content_type = content_type 12 | self.body = body 13 | -------------------------------------------------------------------------------- /codes/chapter17-3/henango/server/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from henango.server.worker import Worker 4 | 5 | 6 | class Server: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = Worker(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter17-3/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # 実行ファイルのあるディレクトリ 4 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | 6 | # 静的配信するファイルを置くディレクトリ 7 | STATIC_ROOT = os.path.join(BASE_DIR, "static") 8 | -------------------------------------------------------------------------------- /codes/chapter17-3/start.py: -------------------------------------------------------------------------------- 1 | from henango.server.server import Server 2 | 3 | if __name__ == "__main__": 4 | Server().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter17-3/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter17-3/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter17-3/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter17-3/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter17-3/static/logo.png -------------------------------------------------------------------------------- /codes/chapter17-3/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | 3 | # pathとview関数の対応 4 | URL_VIEW = { 5 | "/now": views.now, 6 | "/show_request": views.show_request, 7 | "/parameters": views.parameters, 8 | } 9 | -------------------------------------------------------------------------------- /codes/chapter17/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | 8 | def __init__( 9 | self, path: str = "", method: str = "", http_version: str = "", headers: dict = None, body: bytes = b"" 10 | ): 11 | if headers is None: 12 | headers = {} 13 | 14 | self.path = path 15 | self.method = method 16 | self.http_version = http_version 17 | self.headers = headers 18 | self.body = body 19 | -------------------------------------------------------------------------------- /codes/chapter17/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | content_type: Optional[str] 7 | body: bytes 8 | 9 | def __init__(self, status_code: int, content_type: str = None, body: bytes = b""): 10 | self.status_code = status_code 11 | self.content_type = content_type 12 | self.body = body 13 | -------------------------------------------------------------------------------- /codes/chapter17/start.py: -------------------------------------------------------------------------------- 1 | from webserver import WebServer 2 | 3 | if __name__ == "__main__": 4 | WebServer().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter17/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter17/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter17/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter17/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter17/static/logo.png -------------------------------------------------------------------------------- /codes/chapter17/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | 3 | # pathとview関数の対応 4 | URL_VIEW = { 5 | "/now": views.now, 6 | "/show_request": views.show_request, 7 | "/parameters": views.parameters, 8 | } 9 | -------------------------------------------------------------------------------- /codes/chapter17/webserver.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from workerthread import WorkerThread 4 | 5 | 6 | class WebServer: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = WorkerThread(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter18-2/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | params: dict 8 | 9 | def __init__( 10 | self, 11 | path: str = "", 12 | method: str = "", 13 | http_version: str = "", 14 | headers: dict = None, 15 | body: bytes = b"", 16 | params: dict = None, 17 | ): 18 | if headers is None: 19 | headers = {} 20 | if params is None: 21 | params = {} 22 | 23 | self.path = path 24 | self.method = method 25 | self.http_version = http_version 26 | self.headers = headers 27 | self.body = body 28 | self.params = params 29 | -------------------------------------------------------------------------------- /codes/chapter18-2/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | content_type: Optional[str] 7 | body: bytes 8 | 9 | def __init__(self, status_code: int, content_type: str = None, body: bytes = b""): 10 | self.status_code = status_code 11 | self.content_type = content_type 12 | self.body = body 13 | -------------------------------------------------------------------------------- /codes/chapter18-2/henango/server/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from henango.server.worker import Worker 4 | 5 | 6 | class Server: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = Worker(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter18-2/henango/urls/pattern.py: -------------------------------------------------------------------------------- 1 | import re 2 | from re import Match 3 | from typing import Callable, Optional 4 | 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | class URLPattern: 10 | pattern: str 11 | view: Callable[[HTTPRequest], HTTPResponse] 12 | 13 | def __init__(self, pattern: str, view: Callable[[HTTPRequest], HTTPResponse]): 14 | self.pattern = pattern 15 | self.view = view 16 | 17 | def match(self, path: str) -> Optional[Match]: 18 | """ 19 | pathがURLパターンにマッチするか判定する 20 | マッチした場合はMatchオブジェクトを返し、マッチしなかった場合はNoneを返す 21 | """ 22 | # URLパターンを正規表現パターンに変換する 23 | # ex) '/user//profile' => '/user/(?P[^/]+)/profile' 24 | pattern = re.sub(r"<(.+?)>", r"(?P<\1>[^/]+)", self.pattern) 25 | return re.match(pattern, path) 26 | -------------------------------------------------------------------------------- /codes/chapter18-2/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # 実行ファイルのあるディレクトリ 4 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | 6 | # 静的配信するファイルを置くディレクトリ 7 | STATIC_ROOT = os.path.join(BASE_DIR, "static") 8 | -------------------------------------------------------------------------------- /codes/chapter18-2/start.py: -------------------------------------------------------------------------------- 1 | from henango.server.server import Server 2 | 3 | if __name__ == "__main__": 4 | Server().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter18-2/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter18-2/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter18-2/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter18-2/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter18-2/static/logo.png -------------------------------------------------------------------------------- /codes/chapter18-2/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | from henango.urls.pattern import URLPattern 3 | 4 | # pathとview関数の対応 5 | url_patterns = [ 6 | URLPattern("/now", views.now), 7 | URLPattern("/show_request", views.show_request), 8 | URLPattern("/parameters", views.parameters), 9 | URLPattern("/user//profile", views.user_profile), 10 | ] 11 | -------------------------------------------------------------------------------- /codes/chapter18-3/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | params: dict 8 | 9 | def __init__( 10 | self, 11 | path: str = "", 12 | method: str = "", 13 | http_version: str = "", 14 | headers: dict = None, 15 | body: bytes = b"", 16 | params: dict = None, 17 | ): 18 | if headers is None: 19 | headers = {} 20 | if params is None: 21 | params = {} 22 | 23 | self.path = path 24 | self.method = method 25 | self.http_version = http_version 26 | self.headers = headers 27 | self.body = body 28 | self.params = params 29 | -------------------------------------------------------------------------------- /codes/chapter18-3/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | content_type: Optional[str] 7 | body: bytes 8 | 9 | def __init__(self, status_code: int, content_type: str = None, body: bytes = b""): 10 | self.status_code = status_code 11 | self.content_type = content_type 12 | self.body = body 13 | -------------------------------------------------------------------------------- /codes/chapter18-3/henango/server/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from henango.server.worker import Worker 4 | 5 | 6 | class Server: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = Worker(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter18-3/henango/urls/pattern.py: -------------------------------------------------------------------------------- 1 | import re 2 | from re import Match 3 | from typing import Callable, Optional 4 | 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | class URLPattern: 10 | pattern: str 11 | view: Callable[[HTTPRequest], HTTPResponse] 12 | 13 | def __init__(self, pattern: str, view: Callable[[HTTPRequest], HTTPResponse]): 14 | self.pattern = pattern 15 | self.view = view 16 | 17 | def match(self, path: str) -> Optional[Match]: 18 | """ 19 | pathがURLパターンにマッチするか判定する 20 | マッチした場合はMatchオブジェクトを返し、マッチしなかった場合はNoneを返す 21 | """ 22 | # URLパターンを正規表現パターンに変換する 23 | # ex) '/user//profile' => '/user/(?P[^/]+)/profile' 24 | pattern = re.sub(r"<(.+?)>", r"(?P<\1>[^/]+)", self.pattern) 25 | return re.match(pattern, path) 26 | -------------------------------------------------------------------------------- /codes/chapter18-3/henango/urls/resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, Optional 2 | 3 | from henango.http.request import HTTPRequest 4 | from henango.http.response import HTTPResponse 5 | from urls import url_patterns 6 | 7 | 8 | class URLResolver: 9 | def resolve(self, request: HTTPRequest) -> Optional[Callable[[HTTPRequest], HTTPResponse]]: 10 | """ 11 | URL解決を行う 12 | pathにマッチするURLパターンが存在した場合は、対応するviewを返す 13 | 存在しなかった場合は、Noneを返す 14 | """ 15 | for url_pattern in url_patterns: 16 | match = url_pattern.match(request.path) 17 | if match: 18 | request.params.update(match.groupdict()) 19 | return url_pattern.view 20 | 21 | return None 22 | -------------------------------------------------------------------------------- /codes/chapter18-3/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # 実行ファイルのあるディレクトリ 4 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | 6 | # 静的配信するファイルを置くディレクトリ 7 | STATIC_ROOT = os.path.join(BASE_DIR, "static") 8 | -------------------------------------------------------------------------------- /codes/chapter18-3/start.py: -------------------------------------------------------------------------------- 1 | from henango.server.server import Server 2 | 3 | if __name__ == "__main__": 4 | Server().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter18-3/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter18-3/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter18-3/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter18-3/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter18-3/static/logo.png -------------------------------------------------------------------------------- /codes/chapter18-3/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | from henango.urls.pattern import URLPattern 3 | 4 | # pathとview関数の対応 5 | url_patterns = [ 6 | URLPattern("/now", views.now), 7 | URLPattern("/show_request", views.show_request), 8 | URLPattern("/parameters", views.parameters), 9 | URLPattern("/user//profile", views.user_profile), 10 | ] 11 | -------------------------------------------------------------------------------- /codes/chapter18-4/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | params: dict 8 | 9 | def __init__( 10 | self, 11 | path: str = "", 12 | method: str = "", 13 | http_version: str = "", 14 | headers: dict = None, 15 | body: bytes = b"", 16 | params: dict = None, 17 | ): 18 | if headers is None: 19 | headers = {} 20 | if params is None: 21 | params = {} 22 | 23 | self.path = path 24 | self.method = method 25 | self.http_version = http_version 26 | self.headers = headers 27 | self.body = body 28 | self.params = params 29 | -------------------------------------------------------------------------------- /codes/chapter18-4/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | content_type: Optional[str] 7 | body: bytes 8 | 9 | def __init__(self, status_code: int, content_type: str = None, body: bytes = b""): 10 | self.status_code = status_code 11 | self.content_type = content_type 12 | self.body = body 13 | -------------------------------------------------------------------------------- /codes/chapter18-4/henango/server/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from henango.server.worker import Worker 4 | 5 | 6 | class Server: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = Worker(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter18-4/henango/urls/pattern.py: -------------------------------------------------------------------------------- 1 | import re 2 | from re import Match 3 | from typing import Callable, Optional 4 | 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | class URLPattern: 10 | pattern: str 11 | view: Callable[[HTTPRequest], HTTPResponse] 12 | 13 | def __init__(self, pattern: str, view: Callable[[HTTPRequest], HTTPResponse]): 14 | self.pattern = pattern 15 | self.view = view 16 | 17 | def match(self, path: str) -> Optional[Match]: 18 | """ 19 | pathがURLパターンにマッチするか判定する 20 | マッチした場合はMatchオブジェクトを返し、マッチしなかった場合はNoneを返す 21 | """ 22 | # URLパターンを正規表現パターンに変換する 23 | # ex) '/user//profile' => '/user/(?P[^/]+)/profile' 24 | pattern = re.sub(r"<(.+?)>", r"(?P<\1>[^/]+)", self.pattern) 25 | return re.match(pattern, path) 26 | -------------------------------------------------------------------------------- /codes/chapter18-4/henango/urls/resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | from henango.http.request import HTTPRequest 4 | from henango.http.response import HTTPResponse 5 | from henango.views.static import static 6 | from urls import url_patterns 7 | 8 | 9 | class URLResolver: 10 | def resolve(self, request: HTTPRequest) -> Callable[[HTTPRequest], HTTPResponse]: 11 | """ 12 | URL解決を行う 13 | pathにマッチするURLパターンが存在した場合は、対応するviewを返す 14 | 存在しなかった場合は、static viewを返す 15 | """ 16 | for url_pattern in url_patterns: 17 | match = url_pattern.match(request.path) 18 | if match: 19 | request.params.update(match.groupdict()) 20 | return url_pattern.view 21 | 22 | return static 23 | -------------------------------------------------------------------------------- /codes/chapter18-4/henango/views/static.py: -------------------------------------------------------------------------------- 1 | import os 2 | import traceback 3 | 4 | import settings 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | def static(request: HTTPRequest) -> HTTPResponse: 10 | """ 11 | 静的ファイルからレスポンスを取得する 12 | """ 13 | 14 | try: 15 | static_root = getattr(settings, "STATIC_ROOT") 16 | 17 | # pathの先頭の/を削除し、相対パスにしておく 18 | relative_path = request.path.lstrip("/") 19 | # ファイルのpathを取得 20 | static_file_path = os.path.join(static_root, relative_path) 21 | 22 | with open(static_file_path, "rb") as f: 23 | response_body = f.read() 24 | 25 | content_type = None 26 | return HTTPResponse(body=response_body, content_type=content_type, status_code=200) 27 | 28 | except OSError: 29 | # ファイルを取得できなかった場合は、ログを出力して404を返す 30 | traceback.print_exc() 31 | 32 | response_body = b"

404 Not Found

" 33 | content_type = "text/html;" 34 | return HTTPResponse(body=response_body, content_type=content_type, status_code=404) 35 | -------------------------------------------------------------------------------- /codes/chapter18-4/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # 実行ファイルのあるディレクトリ 4 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | 6 | # 静的配信するファイルを置くディレクトリ 7 | STATIC_ROOT = os.path.join(BASE_DIR, "static") 8 | -------------------------------------------------------------------------------- /codes/chapter18-4/start.py: -------------------------------------------------------------------------------- 1 | from henango.server.server import Server 2 | 3 | if __name__ == "__main__": 4 | Server().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter18-4/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter18-4/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter18-4/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter18-4/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter18-4/static/logo.png -------------------------------------------------------------------------------- /codes/chapter18-4/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | from henango.urls.pattern import URLPattern 3 | 4 | # pathとview関数の対応 5 | url_patterns = [ 6 | URLPattern("/now", views.now), 7 | URLPattern("/show_request", views.show_request), 8 | URLPattern("/parameters", views.parameters), 9 | URLPattern("/user//profile", views.user_profile), 10 | ] 11 | -------------------------------------------------------------------------------- /codes/chapter18/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | params: dict 8 | 9 | def __init__( 10 | self, 11 | path: str = "", 12 | method: str = "", 13 | http_version: str = "", 14 | headers: dict = None, 15 | body: bytes = b"", 16 | params: dict = None, 17 | ): 18 | if headers is None: 19 | headers = {} 20 | if params is None: 21 | params = {} 22 | 23 | self.path = path 24 | self.method = method 25 | self.http_version = http_version 26 | self.headers = headers 27 | self.body = body 28 | self.params = params 29 | -------------------------------------------------------------------------------- /codes/chapter18/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | content_type: Optional[str] 7 | body: bytes 8 | 9 | def __init__(self, status_code: int, content_type: str = None, body: bytes = b""): 10 | self.status_code = status_code 11 | self.content_type = content_type 12 | self.body = body 13 | -------------------------------------------------------------------------------- /codes/chapter18/henango/server/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from henango.server.worker import Worker 4 | 5 | 6 | class Server: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = Worker(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter18/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # 実行ファイルのあるディレクトリ 4 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | 6 | # 静的配信するファイルを置くディレクトリ 7 | STATIC_ROOT = os.path.join(BASE_DIR, "static") 8 | -------------------------------------------------------------------------------- /codes/chapter18/start.py: -------------------------------------------------------------------------------- 1 | from henango.server.server import Server 2 | 3 | if __name__ == "__main__": 4 | Server().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter18/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter18/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter18/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter18/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter18/static/logo.png -------------------------------------------------------------------------------- /codes/chapter18/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | 3 | # pathとview関数の対応 4 | URL_VIEW = { 5 | "/now": views.now, 6 | "/show_request": views.show_request, 7 | "/parameters": views.parameters, 8 | "/user//profile": views.user_profile, 9 | } 10 | -------------------------------------------------------------------------------- /codes/chapter19-2/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | params: dict 8 | 9 | def __init__( 10 | self, 11 | path: str = "", 12 | method: str = "", 13 | http_version: str = "", 14 | headers: dict = None, 15 | body: bytes = b"", 16 | params: dict = None, 17 | ): 18 | if headers is None: 19 | headers = {} 20 | if params is None: 21 | params = {} 22 | 23 | self.path = path 24 | self.method = method 25 | self.http_version = http_version 26 | self.headers = headers 27 | self.body = body 28 | self.params = params 29 | -------------------------------------------------------------------------------- /codes/chapter19-2/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | content_type: Optional[str] 7 | body: bytes 8 | 9 | def __init__(self, status_code: int, content_type: str = None, body: bytes = b""): 10 | self.status_code = status_code 11 | self.content_type = content_type 12 | self.body = body 13 | -------------------------------------------------------------------------------- /codes/chapter19-2/henango/server/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from henango.server.worker import Worker 4 | 5 | 6 | class Server: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = Worker(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter19-2/henango/template/renderer.py: -------------------------------------------------------------------------------- 1 | def render(template_path: str, context: dict): 2 | with open(template_path) as f: 3 | template = f.read() 4 | 5 | return template.format(**context) 6 | -------------------------------------------------------------------------------- /codes/chapter19-2/henango/urls/pattern.py: -------------------------------------------------------------------------------- 1 | import re 2 | from re import Match 3 | from typing import Callable, Optional 4 | 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | class URLPattern: 10 | pattern: str 11 | view: Callable[[HTTPRequest], HTTPResponse] 12 | 13 | def __init__(self, pattern: str, view: Callable[[HTTPRequest], HTTPResponse]): 14 | self.pattern = pattern 15 | self.view = view 16 | 17 | def match(self, path: str) -> Optional[Match]: 18 | """ 19 | pathがURLパターンにマッチするか判定する 20 | マッチした場合はMatchオブジェクトを返し、マッチしなかった場合はNoneを返す 21 | """ 22 | # URLパターンを正規表現パターンに変換する 23 | # ex) '/user//profile' => '/user/(?P[^/]+)/profile' 24 | pattern = re.sub(r"<(.+?)>", r"(?P<\1>[^/]+)", self.pattern) 25 | return re.match(pattern, path) 26 | -------------------------------------------------------------------------------- /codes/chapter19-2/henango/urls/resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | from henango.http.request import HTTPRequest 4 | from henango.http.response import HTTPResponse 5 | from henango.views.static import static 6 | from urls import url_patterns 7 | 8 | 9 | class URLResolver: 10 | def resolve(self, request: HTTPRequest) -> Callable[[HTTPRequest], HTTPResponse]: 11 | """ 12 | URL解決を行う 13 | pathにマッチするURLパターンが存在した場合は、対応するviewを返す 14 | 存在しなかった場合は、static viewを返す 15 | """ 16 | for url_pattern in url_patterns: 17 | match = url_pattern.match(request.path) 18 | if match: 19 | request.params.update(match.groupdict()) 20 | return url_pattern.view 21 | 22 | return static 23 | -------------------------------------------------------------------------------- /codes/chapter19-2/henango/views/static.py: -------------------------------------------------------------------------------- 1 | import os 2 | import traceback 3 | 4 | import settings 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | def static(request: HTTPRequest) -> HTTPResponse: 10 | """ 11 | 静的ファイルからレスポンスを取得する 12 | """ 13 | 14 | try: 15 | static_root = getattr(settings, "STATIC_ROOT") 16 | 17 | # pathの先頭の/を削除し、相対パスにしておく 18 | relative_path = request.path.lstrip("/") 19 | # ファイルのpathを取得 20 | static_file_path = os.path.join(static_root, relative_path) 21 | 22 | with open(static_file_path, "rb") as f: 23 | response_body = f.read() 24 | 25 | content_type = None 26 | return HTTPResponse(body=response_body, content_type=content_type, status_code=200) 27 | 28 | except OSError: 29 | # ファイルを取得できなかった場合は、ログを出力して404を返す 30 | traceback.print_exc() 31 | 32 | response_body = b"

404 Not Found

" 33 | content_type = "text/html;" 34 | return HTTPResponse(body=response_body, content_type=content_type, status_code=404) 35 | -------------------------------------------------------------------------------- /codes/chapter19-2/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # 実行ファイルのあるディレクトリ 4 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | 6 | # 静的配信するファイルを置くディレクトリ 7 | STATIC_ROOT = os.path.join(BASE_DIR, "static") 8 | -------------------------------------------------------------------------------- /codes/chapter19-2/start.py: -------------------------------------------------------------------------------- 1 | from henango.server.server import Server 2 | 3 | if __name__ == "__main__": 4 | Server().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter19-2/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter19-2/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter19-2/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter19-2/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter19-2/static/logo.png -------------------------------------------------------------------------------- /codes/chapter19-2/templates/now.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Now: {now}

4 | 5 | -------------------------------------------------------------------------------- /codes/chapter19-2/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | from henango.urls.pattern import URLPattern 3 | 4 | # pathとview関数の対応 5 | url_patterns = [ 6 | URLPattern("/now", views.now), 7 | URLPattern("/show_request", views.show_request), 8 | URLPattern("/parameters", views.parameters), 9 | URLPattern("/user//profile", views.user_profile), 10 | ] 11 | -------------------------------------------------------------------------------- /codes/chapter19-3/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | params: dict 8 | 9 | def __init__( 10 | self, 11 | path: str = "", 12 | method: str = "", 13 | http_version: str = "", 14 | headers: dict = None, 15 | body: bytes = b"", 16 | params: dict = None, 17 | ): 18 | if headers is None: 19 | headers = {} 20 | if params is None: 21 | params = {} 22 | 23 | self.path = path 24 | self.method = method 25 | self.http_version = http_version 26 | self.headers = headers 27 | self.body = body 28 | self.params = params 29 | -------------------------------------------------------------------------------- /codes/chapter19-3/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | content_type: Optional[str] 7 | body: bytes 8 | 9 | def __init__(self, status_code: int, content_type: str = None, body: bytes = b""): 10 | self.status_code = status_code 11 | self.content_type = content_type 12 | self.body = body 13 | -------------------------------------------------------------------------------- /codes/chapter19-3/henango/server/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from henango.server.worker import Worker 4 | 5 | 6 | class Server: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = Worker(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter19-3/henango/template/renderer.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import settings 4 | 5 | 6 | def render(template_name: str, context: dict): 7 | template_path = os.path.join(settings.TEMPLATES_DIR, template_name) 8 | 9 | with open(template_path) as f: 10 | template = f.read() 11 | 12 | return template.format(**context) 13 | -------------------------------------------------------------------------------- /codes/chapter19-3/henango/urls/pattern.py: -------------------------------------------------------------------------------- 1 | import re 2 | from re import Match 3 | from typing import Callable, Optional 4 | 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | class URLPattern: 10 | pattern: str 11 | view: Callable[[HTTPRequest], HTTPResponse] 12 | 13 | def __init__(self, pattern: str, view: Callable[[HTTPRequest], HTTPResponse]): 14 | self.pattern = pattern 15 | self.view = view 16 | 17 | def match(self, path: str) -> Optional[Match]: 18 | """ 19 | pathがURLパターンにマッチするか判定する 20 | マッチした場合はMatchオブジェクトを返し、マッチしなかった場合はNoneを返す 21 | """ 22 | # URLパターンを正規表現パターンに変換する 23 | # ex) '/user//profile' => '/user/(?P[^/]+)/profile' 24 | pattern = re.sub(r"<(.+?)>", r"(?P<\1>[^/]+)", self.pattern) 25 | return re.match(pattern, path) 26 | -------------------------------------------------------------------------------- /codes/chapter19-3/henango/urls/resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | from henango.http.request import HTTPRequest 4 | from henango.http.response import HTTPResponse 5 | from henango.views.static import static 6 | from urls import url_patterns 7 | 8 | 9 | class URLResolver: 10 | def resolve(self, request: HTTPRequest) -> Callable[[HTTPRequest], HTTPResponse]: 11 | """ 12 | URL解決を行う 13 | pathにマッチするURLパターンが存在した場合は、対応するviewを返す 14 | 存在しなかった場合は、static viewを返す 15 | """ 16 | for url_pattern in url_patterns: 17 | match = url_pattern.match(request.path) 18 | if match: 19 | request.params.update(match.groupdict()) 20 | return url_pattern.view 21 | 22 | return static 23 | -------------------------------------------------------------------------------- /codes/chapter19-3/henango/views/static.py: -------------------------------------------------------------------------------- 1 | import os 2 | import traceback 3 | 4 | import settings 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | def static(request: HTTPRequest) -> HTTPResponse: 10 | """ 11 | 静的ファイルからレスポンスを取得する 12 | """ 13 | 14 | try: 15 | static_root = getattr(settings, "STATIC_ROOT") 16 | 17 | # pathの先頭の/を削除し、相対パスにしておく 18 | relative_path = request.path.lstrip("/") 19 | # ファイルのpathを取得 20 | static_file_path = os.path.join(static_root, relative_path) 21 | 22 | with open(static_file_path, "rb") as f: 23 | response_body = f.read() 24 | 25 | content_type = None 26 | return HTTPResponse(body=response_body, content_type=content_type, status_code=200) 27 | 28 | except OSError: 29 | # ファイルを取得できなかった場合は、ログを出力して404を返す 30 | traceback.print_exc() 31 | 32 | response_body = b"

404 Not Found

" 33 | content_type = "text/html;" 34 | return HTTPResponse(body=response_body, content_type=content_type, status_code=404) 35 | -------------------------------------------------------------------------------- /codes/chapter19-3/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # 実行ファイルのあるディレクトリ 4 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | 6 | # 静的配信するファイルを置くディレクトリ 7 | STATIC_ROOT = os.path.join(BASE_DIR, "static") 8 | 9 | # テンプレートファイルを置くディレクトリ 10 | TEMPLATES_DIR = os.path.join(BASE_DIR, "templates") 11 | -------------------------------------------------------------------------------- /codes/chapter19-3/start.py: -------------------------------------------------------------------------------- 1 | from henango.server.server import Server 2 | 3 | if __name__ == "__main__": 4 | Server().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter19-3/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter19-3/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter19-3/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter19-3/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter19-3/static/logo.png -------------------------------------------------------------------------------- /codes/chapter19-3/templates/now.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Now: {now}

4 | 5 | -------------------------------------------------------------------------------- /codes/chapter19-3/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | from henango.urls.pattern import URLPattern 3 | 4 | # pathとview関数の対応 5 | url_patterns = [ 6 | URLPattern("/now", views.now), 7 | URLPattern("/show_request", views.show_request), 8 | URLPattern("/parameters", views.parameters), 9 | URLPattern("/user//profile", views.user_profile), 10 | ] 11 | -------------------------------------------------------------------------------- /codes/chapter19-4/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | params: dict 8 | 9 | def __init__( 10 | self, 11 | path: str = "", 12 | method: str = "", 13 | http_version: str = "", 14 | headers: dict = None, 15 | body: bytes = b"", 16 | params: dict = None, 17 | ): 18 | if headers is None: 19 | headers = {} 20 | if params is None: 21 | params = {} 22 | 23 | self.path = path 24 | self.method = method 25 | self.http_version = http_version 26 | self.headers = headers 27 | self.body = body 28 | self.params = params 29 | -------------------------------------------------------------------------------- /codes/chapter19-4/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Union 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | content_type: Optional[str] 7 | body: Union[bytes, str] 8 | 9 | def __init__(self, status_code: int, content_type: str = None, body: Union[bytes, str] = b""): 10 | self.status_code = status_code 11 | self.content_type = content_type 12 | self.body = body 13 | -------------------------------------------------------------------------------- /codes/chapter19-4/henango/server/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from henango.server.worker import Worker 4 | 5 | 6 | class Server: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = Worker(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter19-4/henango/template/renderer.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import settings 4 | 5 | 6 | def render(template_name: str, context: dict): 7 | template_path = os.path.join(settings.TEMPLATES_DIR, template_name) 8 | 9 | with open(template_path) as f: 10 | template = f.read() 11 | 12 | return template.format(**context) 13 | -------------------------------------------------------------------------------- /codes/chapter19-4/henango/urls/pattern.py: -------------------------------------------------------------------------------- 1 | import re 2 | from re import Match 3 | from typing import Callable, Optional 4 | 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | class URLPattern: 10 | pattern: str 11 | view: Callable[[HTTPRequest], HTTPResponse] 12 | 13 | def __init__(self, pattern: str, view: Callable[[HTTPRequest], HTTPResponse]): 14 | self.pattern = pattern 15 | self.view = view 16 | 17 | def match(self, path: str) -> Optional[Match]: 18 | """ 19 | pathがURLパターンにマッチするか判定する 20 | マッチした場合はMatchオブジェクトを返し、マッチしなかった場合はNoneを返す 21 | """ 22 | # URLパターンを正規表現パターンに変換する 23 | # ex) '/user//profile' => '/user/(?P[^/]+)/profile' 24 | pattern = re.sub(r"<(.+?)>", r"(?P<\1>[^/]+)", self.pattern) 25 | return re.match(pattern, path) 26 | -------------------------------------------------------------------------------- /codes/chapter19-4/henango/urls/resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | from henango.http.request import HTTPRequest 4 | from henango.http.response import HTTPResponse 5 | from henango.views.static import static 6 | from urls import url_patterns 7 | 8 | 9 | class URLResolver: 10 | def resolve(self, request: HTTPRequest) -> Callable[[HTTPRequest], HTTPResponse]: 11 | """ 12 | URL解決を行う 13 | pathにマッチするURLパターンが存在した場合は、対応するviewを返す 14 | 存在しなかった場合は、static viewを返す 15 | """ 16 | for url_pattern in url_patterns: 17 | match = url_pattern.match(request.path) 18 | if match: 19 | request.params.update(match.groupdict()) 20 | return url_pattern.view 21 | 22 | return static 23 | -------------------------------------------------------------------------------- /codes/chapter19-4/henango/views/static.py: -------------------------------------------------------------------------------- 1 | import os 2 | import traceback 3 | 4 | import settings 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | def static(request: HTTPRequest) -> HTTPResponse: 10 | """ 11 | 静的ファイルからレスポンスを取得する 12 | """ 13 | 14 | try: 15 | static_root = getattr(settings, "STATIC_ROOT") 16 | 17 | # pathの先頭の/を削除し、相対パスにしておく 18 | relative_path = request.path.lstrip("/") 19 | # ファイルのpathを取得 20 | static_file_path = os.path.join(static_root, relative_path) 21 | 22 | with open(static_file_path, "rb") as f: 23 | response_body = f.read() 24 | 25 | content_type = None 26 | return HTTPResponse(body=response_body, content_type=content_type, status_code=200) 27 | 28 | except OSError: 29 | # ファイルを取得できなかった場合は、ログを出力して404を返す 30 | traceback.print_exc() 31 | 32 | response_body = b"

404 Not Found

" 33 | content_type = "text/html;" 34 | return HTTPResponse(body=response_body, content_type=content_type, status_code=404) 35 | -------------------------------------------------------------------------------- /codes/chapter19-4/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # 実行ファイルのあるディレクトリ 4 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | 6 | # 静的配信するファイルを置くディレクトリ 7 | STATIC_ROOT = os.path.join(BASE_DIR, "static") 8 | 9 | # テンプレートファイルを置くディレクトリ 10 | TEMPLATES_DIR = os.path.join(BASE_DIR, "templates") 11 | -------------------------------------------------------------------------------- /codes/chapter19-4/start.py: -------------------------------------------------------------------------------- 1 | from henango.server.server import Server 2 | 3 | if __name__ == "__main__": 4 | Server().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter19-4/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter19-4/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter19-4/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter19-4/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter19-4/static/logo.png -------------------------------------------------------------------------------- /codes/chapter19-4/templates/now.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Now: {now}

4 | 5 | -------------------------------------------------------------------------------- /codes/chapter19-4/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | from henango.urls.pattern import URLPattern 3 | 4 | # pathとview関数の対応 5 | url_patterns = [ 6 | URLPattern("/now", views.now), 7 | URLPattern("/show_request", views.show_request), 8 | URLPattern("/parameters", views.parameters), 9 | URLPattern("/user//profile", views.user_profile), 10 | ] 11 | -------------------------------------------------------------------------------- /codes/chapter19-5/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | params: dict 8 | 9 | def __init__( 10 | self, 11 | path: str = "", 12 | method: str = "", 13 | http_version: str = "", 14 | headers: dict = None, 15 | body: bytes = b"", 16 | params: dict = None, 17 | ): 18 | if headers is None: 19 | headers = {} 20 | if params is None: 21 | params = {} 22 | 23 | self.path = path 24 | self.method = method 25 | self.http_version = http_version 26 | self.headers = headers 27 | self.body = body 28 | self.params = params 29 | -------------------------------------------------------------------------------- /codes/chapter19-5/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Union 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | content_type: Optional[str] 7 | body: Union[bytes, str] 8 | 9 | def __init__(self, status_code: int = 200, content_type: str = None, body: Union[bytes, str] = b""): 10 | self.status_code = status_code 11 | self.content_type = content_type 12 | self.body = body 13 | -------------------------------------------------------------------------------- /codes/chapter19-5/henango/server/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from henango.server.worker import Worker 4 | 5 | 6 | class Server: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = Worker(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter19-5/henango/template/renderer.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import settings 4 | 5 | 6 | def render(template_name: str, context: dict): 7 | template_path = os.path.join(settings.TEMPLATES_DIR, template_name) 8 | 9 | with open(template_path) as f: 10 | template = f.read() 11 | 12 | return template.format(**context) 13 | -------------------------------------------------------------------------------- /codes/chapter19-5/henango/urls/pattern.py: -------------------------------------------------------------------------------- 1 | import re 2 | from re import Match 3 | from typing import Callable, Optional 4 | 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | class URLPattern: 10 | pattern: str 11 | view: Callable[[HTTPRequest], HTTPResponse] 12 | 13 | def __init__(self, pattern: str, view: Callable[[HTTPRequest], HTTPResponse]): 14 | self.pattern = pattern 15 | self.view = view 16 | 17 | def match(self, path: str) -> Optional[Match]: 18 | """ 19 | pathがURLパターンにマッチするか判定する 20 | マッチした場合はMatchオブジェクトを返し、マッチしなかった場合はNoneを返す 21 | """ 22 | # URLパターンを正規表現パターンに変換する 23 | # ex) '/user//profile' => '/user/(?P[^/]+)/profile' 24 | pattern = re.sub(r"<(.+?)>", r"(?P<\1>[^/]+)", self.pattern) 25 | return re.match(pattern, path) 26 | -------------------------------------------------------------------------------- /codes/chapter19-5/henango/urls/resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | from henango.http.request import HTTPRequest 4 | from henango.http.response import HTTPResponse 5 | from henango.views.static import static 6 | from urls import url_patterns 7 | 8 | 9 | class URLResolver: 10 | def resolve(self, request: HTTPRequest) -> Callable[[HTTPRequest], HTTPResponse]: 11 | """ 12 | URL解決を行う 13 | pathにマッチするURLパターンが存在した場合は、対応するviewを返す 14 | 存在しなかった場合は、static viewを返す 15 | """ 16 | for url_pattern in url_patterns: 17 | match = url_pattern.match(request.path) 18 | if match: 19 | request.params.update(match.groupdict()) 20 | return url_pattern.view 21 | 22 | return static 23 | -------------------------------------------------------------------------------- /codes/chapter19-5/henango/views/static.py: -------------------------------------------------------------------------------- 1 | import os 2 | import traceback 3 | 4 | import settings 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | def static(request: HTTPRequest) -> HTTPResponse: 10 | """ 11 | 静的ファイルからレスポンスを取得する 12 | """ 13 | 14 | try: 15 | static_root = getattr(settings, "STATIC_ROOT") 16 | 17 | # pathの先頭の/を削除し、相対パスにしておく 18 | relative_path = request.path.lstrip("/") 19 | # ファイルのpathを取得 20 | static_file_path = os.path.join(static_root, relative_path) 21 | 22 | with open(static_file_path, "rb") as f: 23 | response_body = f.read() 24 | 25 | content_type = None 26 | return HTTPResponse(body=response_body, content_type=content_type, status_code=200) 27 | 28 | except OSError: 29 | # ファイルを取得できなかった場合は、ログを出力して404を返す 30 | traceback.print_exc() 31 | 32 | response_body = b"

404 Not Found

" 33 | content_type = "text/html;" 34 | return HTTPResponse(body=response_body, content_type=content_type, status_code=404) 35 | -------------------------------------------------------------------------------- /codes/chapter19-5/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # 実行ファイルのあるディレクトリ 4 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | 6 | # 静的配信するファイルを置くディレクトリ 7 | STATIC_ROOT = os.path.join(BASE_DIR, "static") 8 | 9 | # テンプレートファイルを置くディレクトリ 10 | TEMPLATES_DIR = os.path.join(BASE_DIR, "templates") 11 | -------------------------------------------------------------------------------- /codes/chapter19-5/start.py: -------------------------------------------------------------------------------- 1 | from henango.server.server import Server 2 | 3 | if __name__ == "__main__": 4 | Server().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter19-5/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter19-5/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter19-5/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter19-5/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter19-5/static/logo.png -------------------------------------------------------------------------------- /codes/chapter19-5/templates/now.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Now: {now}

4 | 5 | -------------------------------------------------------------------------------- /codes/chapter19-5/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | from henango.urls.pattern import URLPattern 3 | 4 | # pathとview関数の対応 5 | url_patterns = [ 6 | URLPattern("/now", views.now), 7 | URLPattern("/show_request", views.show_request), 8 | URLPattern("/parameters", views.parameters), 9 | URLPattern("/user//profile", views.user_profile), 10 | ] 11 | -------------------------------------------------------------------------------- /codes/chapter19-6/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | params: dict 8 | 9 | def __init__( 10 | self, 11 | path: str = "", 12 | method: str = "", 13 | http_version: str = "", 14 | headers: dict = None, 15 | body: bytes = b"", 16 | params: dict = None, 17 | ): 18 | if headers is None: 19 | headers = {} 20 | if params is None: 21 | params = {} 22 | 23 | self.path = path 24 | self.method = method 25 | self.http_version = http_version 26 | self.headers = headers 27 | self.body = body 28 | self.params = params 29 | -------------------------------------------------------------------------------- /codes/chapter19-6/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Union 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | content_type: Optional[str] 7 | body: Union[bytes, str] 8 | 9 | def __init__(self, status_code: int = 200, content_type: str = None, body: Union[bytes, str] = b""): 10 | self.status_code = status_code 11 | self.content_type = content_type 12 | self.body = body 13 | -------------------------------------------------------------------------------- /codes/chapter19-6/henango/server/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from henango.server.worker import Worker 4 | 5 | 6 | class Server: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = Worker(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter19-6/henango/template/renderer.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import settings 4 | 5 | 6 | def render(template_name: str, context): 7 | template_path = os.path.join(settings.TEMPLATES_DIR, template_name) 8 | 9 | with open(template_path) as f: 10 | template = f.read() 11 | 12 | return template.format(**context) 13 | -------------------------------------------------------------------------------- /codes/chapter19-6/henango/urls/pattern.py: -------------------------------------------------------------------------------- 1 | import re 2 | from re import Match 3 | from typing import Callable, Optional 4 | 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | class URLPattern: 10 | pattern: str 11 | view: Callable[[HTTPRequest], HTTPResponse] 12 | 13 | def __init__(self, pattern: str, view: Callable[[HTTPRequest], HTTPResponse]): 14 | self.pattern = pattern 15 | self.view = view 16 | 17 | def match(self, path: str) -> Optional[Match]: 18 | """ 19 | pathがURLパターンにマッチするか判定する 20 | マッチした場合はMatchオブジェクトを返し、マッチしなかった場合はNoneを返す 21 | """ 22 | # URLパターンを正規表現パターンに変換する 23 | # ex) '/user//profile' => '/user/(?P[^/]+)/profile' 24 | pattern = re.sub(r"<(.+?)>", r"(?P<\1>[^/]+)", self.pattern) 25 | return re.match(pattern, path) 26 | -------------------------------------------------------------------------------- /codes/chapter19-6/henango/urls/resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | from henango.http.request import HTTPRequest 4 | from henango.http.response import HTTPResponse 5 | from henango.views.static import static 6 | from urls import url_patterns 7 | 8 | 9 | class URLResolver: 10 | def resolve(self, request: HTTPRequest) -> Callable[[HTTPRequest], HTTPResponse]: 11 | """ 12 | URL解決を行う 13 | pathにマッチするURLパターンが存在した場合は、対応するviewを返す 14 | 存在しなかった場合は、static viewを返す 15 | """ 16 | for url_pattern in url_patterns: 17 | match = url_pattern.match(request.path) 18 | if match: 19 | request.params.update(match.groupdict()) 20 | return url_pattern.view 21 | 22 | return static 23 | -------------------------------------------------------------------------------- /codes/chapter19-6/henango/views/static.py: -------------------------------------------------------------------------------- 1 | import os 2 | import traceback 3 | 4 | import settings 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | def static(request: HTTPRequest) -> HTTPResponse: 10 | """ 11 | 静的ファイルからレスポンスを取得する 12 | """ 13 | 14 | try: 15 | static_root = getattr(settings, "STATIC_ROOT") 16 | 17 | # pathの先頭の/を削除し、相対パスにしておく 18 | relative_path = request.path.lstrip("/") 19 | # ファイルのpathを取得 20 | static_file_path = os.path.join(static_root, relative_path) 21 | 22 | with open(static_file_path, "rb") as f: 23 | response_body = f.read() 24 | 25 | content_type = None 26 | return HTTPResponse(body=response_body, content_type=content_type, status_code=200) 27 | 28 | except OSError: 29 | # ファイルを取得できなかった場合は、ログを出力して404を返す 30 | traceback.print_exc() 31 | 32 | response_body = b"

404 Not Found

" 33 | content_type = "text/html;" 34 | return HTTPResponse(body=response_body, content_type=content_type, status_code=404) 35 | -------------------------------------------------------------------------------- /codes/chapter19-6/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # 実行ファイルのあるディレクトリ 4 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | 6 | # 静的配信するファイルを置くディレクトリ 7 | STATIC_ROOT = os.path.join(BASE_DIR, "static") 8 | 9 | # テンプレートファイルを置くディレクトリ 10 | TEMPLATES_DIR = os.path.join(BASE_DIR, "templates") 11 | -------------------------------------------------------------------------------- /codes/chapter19-6/start.py: -------------------------------------------------------------------------------- 1 | from henango.server.server import Server 2 | 3 | if __name__ == "__main__": 4 | Server().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter19-6/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter19-6/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter19-6/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter19-6/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter19-6/static/logo.png -------------------------------------------------------------------------------- /codes/chapter19-6/templates/now.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Now: {now}

4 | 5 | -------------------------------------------------------------------------------- /codes/chapter19-6/templates/parameters.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Parameters:

4 |
{params}
5 | 6 | -------------------------------------------------------------------------------- /codes/chapter19-6/templates/show_request.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Request Line:

4 |

5 | {request.method} {request.path} {request.http_version} 6 |

7 |

Headers:

8 |
{headers}
9 |

Body:

10 |
{body}
11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter19-6/templates/user_profile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

プロフィール

4 |

ID: {user_id} 5 | 6 | -------------------------------------------------------------------------------- /codes/chapter19-6/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | from henango.urls.pattern import URLPattern 3 | 4 | # pathとview関数の対応 5 | url_patterns = [ 6 | URLPattern("/now", views.now), 7 | URLPattern("/show_request", views.show_request), 8 | URLPattern("/parameters", views.parameters), 9 | URLPattern("/user//profile", views.user_profile), 10 | ] 11 | -------------------------------------------------------------------------------- /codes/chapter19-6/views.py: -------------------------------------------------------------------------------- 1 | import urllib.parse 2 | from datetime import datetime 3 | from pprint import pformat 4 | 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | from henango.template.renderer import render 8 | 9 | 10 | def now(request: HTTPRequest) -> HTTPResponse: 11 | """ 12 | 現在時刻を表示するHTMLを生成する 13 | """ 14 | context = {"now": datetime.now()} 15 | body = render("now.html", context) 16 | 17 | return HTTPResponse(body=body) 18 | 19 | 20 | def show_request(request: HTTPRequest) -> HTTPResponse: 21 | """ 22 | HTTPリクエストの内容を表示するHTMLを生成する 23 | """ 24 | context = {"request": request, "headers": pformat(request.headers), "body": request.body.decode("utf-8", "ignore")} 25 | body = render("show_request.html", context) 26 | 27 | return HTTPResponse(body=body) 28 | 29 | 30 | def parameters(request: HTTPRequest) -> HTTPResponse: 31 | """ 32 | POSTパラメータを表示するHTMLを表示する 33 | """ 34 | 35 | # GETリクエストの場合は、405を返す 36 | if request.method == "GET": 37 | body = b"

405 Method Not Allowed

" 38 | 39 | return HTTPResponse(body=body, status_code=405) 40 | 41 | elif request.method == "POST": 42 | context = {"params": urllib.parse.parse_qs(request.body.decode())} 43 | body = render("parameters.html", context) 44 | 45 | return HTTPResponse(body=body) 46 | 47 | 48 | def user_profile(request: HTTPRequest) -> HTTPResponse: 49 | context = {"user_id": request.params["user_id"]} 50 | 51 | body = render("user_profile.html", context) 52 | 53 | return HTTPResponse(body=body) 54 | -------------------------------------------------------------------------------- /codes/chapter19/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | params: dict 8 | 9 | def __init__( 10 | self, 11 | path: str = "", 12 | method: str = "", 13 | http_version: str = "", 14 | headers: dict = None, 15 | body: bytes = b"", 16 | params: dict = None, 17 | ): 18 | if headers is None: 19 | headers = {} 20 | if params is None: 21 | params = {} 22 | 23 | self.path = path 24 | self.method = method 25 | self.http_version = http_version 26 | self.headers = headers 27 | self.body = body 28 | self.params = params 29 | -------------------------------------------------------------------------------- /codes/chapter19/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | content_type: Optional[str] 7 | body: bytes 8 | 9 | def __init__(self, status_code: int, content_type: str = None, body: bytes = b""): 10 | self.status_code = status_code 11 | self.content_type = content_type 12 | self.body = body 13 | -------------------------------------------------------------------------------- /codes/chapter19/henango/server/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from henango.server.worker import Worker 4 | 5 | 6 | class Server: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = Worker(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter19/henango/urls/pattern.py: -------------------------------------------------------------------------------- 1 | import re 2 | from re import Match 3 | from typing import Callable, Optional 4 | 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | class URLPattern: 10 | pattern: str 11 | view: Callable[[HTTPRequest], HTTPResponse] 12 | 13 | def __init__(self, pattern: str, view: Callable[[HTTPRequest], HTTPResponse]): 14 | self.pattern = pattern 15 | self.view = view 16 | 17 | def match(self, path: str) -> Optional[Match]: 18 | """ 19 | pathがURLパターンにマッチするか判定する 20 | マッチした場合はMatchオブジェクトを返し、マッチしなかった場合はNoneを返す 21 | """ 22 | # URLパターンを正規表現パターンに変換する 23 | # ex) '/user//profile' => '/user/(?P[^/]+)/profile' 24 | pattern = re.sub(r"<(.+?)>", r"(?P<\1>[^/]+)", self.pattern) 25 | return re.match(pattern, path) 26 | -------------------------------------------------------------------------------- /codes/chapter19/henango/urls/resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | from henango.http.request import HTTPRequest 4 | from henango.http.response import HTTPResponse 5 | from henango.views.static import static 6 | from urls import url_patterns 7 | 8 | 9 | class URLResolver: 10 | def resolve(self, request: HTTPRequest) -> Callable[[HTTPRequest], HTTPResponse]: 11 | """ 12 | URL解決を行う 13 | pathにマッチするURLパターンが存在した場合は、対応するviewを返す 14 | 存在しなかった場合は、static viewを返す 15 | """ 16 | for url_pattern in url_patterns: 17 | match = url_pattern.match(request.path) 18 | if match: 19 | request.params.update(match.groupdict()) 20 | return url_pattern.view 21 | 22 | return static 23 | -------------------------------------------------------------------------------- /codes/chapter19/henango/views/static.py: -------------------------------------------------------------------------------- 1 | import os 2 | import traceback 3 | 4 | import settings 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | def static(request: HTTPRequest) -> HTTPResponse: 10 | """ 11 | 静的ファイルからレスポンスを取得する 12 | """ 13 | 14 | try: 15 | static_root = getattr(settings, "STATIC_ROOT") 16 | 17 | # pathの先頭の/を削除し、相対パスにしておく 18 | relative_path = request.path.lstrip("/") 19 | # ファイルのpathを取得 20 | static_file_path = os.path.join(static_root, relative_path) 21 | 22 | with open(static_file_path, "rb") as f: 23 | response_body = f.read() 24 | 25 | content_type = None 26 | return HTTPResponse(body=response_body, content_type=content_type, status_code=200) 27 | 28 | except OSError: 29 | # ファイルを取得できなかった場合は、ログを出力して404を返す 30 | traceback.print_exc() 31 | 32 | response_body = b"

404 Not Found

" 33 | content_type = "text/html;" 34 | return HTTPResponse(body=response_body, content_type=content_type, status_code=404) 35 | -------------------------------------------------------------------------------- /codes/chapter19/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # 実行ファイルのあるディレクトリ 4 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | 6 | # 静的配信するファイルを置くディレクトリ 7 | STATIC_ROOT = os.path.join(BASE_DIR, "static") 8 | -------------------------------------------------------------------------------- /codes/chapter19/start.py: -------------------------------------------------------------------------------- 1 | from henango.server.server import Server 2 | 3 | if __name__ == "__main__": 4 | Server().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter19/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter19/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter19/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter19/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter19/static/logo.png -------------------------------------------------------------------------------- /codes/chapter19/templates/now.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Now: {now}

4 | 5 | 6 | -------------------------------------------------------------------------------- /codes/chapter19/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | from henango.urls.pattern import URLPattern 3 | 4 | # pathとview関数の対応 5 | url_patterns = [ 6 | URLPattern("/now", views.now), 7 | URLPattern("/show_request", views.show_request), 8 | URLPattern("/parameters", views.parameters), 9 | URLPattern("/user//profile", views.user_profile), 10 | ] 11 | -------------------------------------------------------------------------------- /codes/chapter20-2/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | params: dict 8 | 9 | def __init__( 10 | self, 11 | path: str = "", 12 | method: str = "", 13 | http_version: str = "", 14 | headers: dict = None, 15 | body: bytes = b"", 16 | params: dict = None, 17 | ): 18 | if headers is None: 19 | headers = {} 20 | if params is None: 21 | params = {} 22 | 23 | self.path = path 24 | self.method = method 25 | self.http_version = http_version 26 | self.headers = headers 27 | self.body = body 28 | self.params = params 29 | -------------------------------------------------------------------------------- /codes/chapter20-2/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Union, Dict 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | headers: dict 7 | content_type: Optional[str] 8 | body: Union[bytes, str] 9 | 10 | def __init__( 11 | self, 12 | status_code: int = 200, 13 | headers: dict = None, 14 | content_type: str = None, 15 | body: Union[bytes, str] = b"", 16 | ): 17 | if headers is None: 18 | headers = {} 19 | 20 | self.status_code = status_code 21 | self.headers = headers 22 | self.content_type = content_type 23 | self.body = body 24 | -------------------------------------------------------------------------------- /codes/chapter20-2/henango/server/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from henango.server.worker import Worker 4 | 5 | 6 | class Server: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = Worker(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter20-2/henango/template/renderer.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import settings 4 | 5 | 6 | def render(template_name: str, context): 7 | template_path = os.path.join(settings.TEMPLATES_DIR, template_name) 8 | 9 | with open(template_path) as f: 10 | template = f.read() 11 | 12 | return template.format(**context) 13 | -------------------------------------------------------------------------------- /codes/chapter20-2/henango/urls/pattern.py: -------------------------------------------------------------------------------- 1 | import re 2 | from re import Match 3 | from typing import Callable, Optional 4 | 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | class URLPattern: 10 | pattern: str 11 | view: Callable[[HTTPRequest], HTTPResponse] 12 | 13 | def __init__(self, pattern: str, view: Callable[[HTTPRequest], HTTPResponse]): 14 | self.pattern = pattern 15 | self.view = view 16 | 17 | def match(self, path: str) -> Optional[Match]: 18 | """ 19 | pathがURLパターンにマッチするか判定する 20 | マッチした場合はMatchオブジェクトを返し、マッチしなかった場合はNoneを返す 21 | """ 22 | # URLパターンを正規表現パターンに変換する 23 | # ex) '/user//profile' => '/user/(?P[^/]+)/profile' 24 | pattern = re.sub(r"<(.+?)>", r"(?P<\1>[^/]+)", self.pattern) 25 | return re.match(pattern, path) 26 | -------------------------------------------------------------------------------- /codes/chapter20-2/henango/urls/resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | from henango.http.request import HTTPRequest 4 | from henango.http.response import HTTPResponse 5 | from henango.views.static import static 6 | from urls import url_patterns 7 | 8 | 9 | class URLResolver: 10 | def resolve(self, request: HTTPRequest) -> Callable[[HTTPRequest], HTTPResponse]: 11 | """ 12 | URL解決を行う 13 | pathにマッチするURLパターンが存在した場合は、対応するviewを返す 14 | 存在しなかった場合は、static viewを返す 15 | """ 16 | for url_pattern in url_patterns: 17 | match = url_pattern.match(request.path) 18 | if match: 19 | request.params.update(match.groupdict()) 20 | return url_pattern.view 21 | 22 | return static 23 | -------------------------------------------------------------------------------- /codes/chapter20-2/henango/views/static.py: -------------------------------------------------------------------------------- 1 | import os 2 | import traceback 3 | 4 | import settings 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | def static(request: HTTPRequest) -> HTTPResponse: 10 | """ 11 | 静的ファイルからレスポンスを取得する 12 | """ 13 | 14 | try: 15 | static_root = getattr(settings, "STATIC_ROOT") 16 | 17 | # pathの先頭の/を削除し、相対パスにしておく 18 | relative_path = request.path.lstrip("/") 19 | # ファイルのpathを取得 20 | static_file_path = os.path.join(static_root, relative_path) 21 | 22 | with open(static_file_path, "rb") as f: 23 | response_body = f.read() 24 | 25 | content_type = None 26 | return HTTPResponse(body=response_body, content_type=content_type, status_code=200) 27 | 28 | except OSError: 29 | # ファイルを取得できなかった場合は、ログを出力して404を返す 30 | traceback.print_exc() 31 | 32 | response_body = b"

404 Not Found

" 33 | content_type = "text/html;" 34 | return HTTPResponse(body=response_body, content_type=content_type, status_code=404) 35 | -------------------------------------------------------------------------------- /codes/chapter20-2/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # 実行ファイルのあるディレクトリ 4 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | 6 | # 静的配信するファイルを置くディレクトリ 7 | STATIC_ROOT = os.path.join(BASE_DIR, "static") 8 | 9 | # テンプレートファイルを置くディレクトリ 10 | TEMPLATES_DIR = os.path.join(BASE_DIR, "templates") 11 | -------------------------------------------------------------------------------- /codes/chapter20-2/start.py: -------------------------------------------------------------------------------- 1 | from henango.server.server import Server 2 | 3 | if __name__ == "__main__": 4 | Server().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter20-2/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter20-2/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter20-2/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter20-2/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter20-2/static/logo.png -------------------------------------------------------------------------------- /codes/chapter20-2/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

【ログイン】名前を入力してください

4 | 5 |
6 | 名前: 7 | 8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /codes/chapter20-2/templates/now.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Now: {now}

4 | 5 | -------------------------------------------------------------------------------- /codes/chapter20-2/templates/parameters.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Parameters:

4 |
{params}
5 | 6 | -------------------------------------------------------------------------------- /codes/chapter20-2/templates/show_request.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Request Line:

4 |

5 | {request.method} {request.path} {request.http_version} 6 |

7 |

Headers:

8 |
{headers}
9 |

Body:

10 |
{body}
11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter20-2/templates/user_profile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

プロフィール

4 |

ID: {user_id} 5 | 6 | -------------------------------------------------------------------------------- /codes/chapter20-2/templates/welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

ようこそ! {username} さん!

4 | 5 | -------------------------------------------------------------------------------- /codes/chapter20-2/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | from henango.urls.pattern import URLPattern 3 | 4 | # pathとview関数の対応 5 | url_patterns = [ 6 | URLPattern("/now", views.now), 7 | URLPattern("/show_request", views.show_request), 8 | URLPattern("/parameters", views.parameters), 9 | URLPattern("/user//profile", views.user_profile), 10 | URLPattern("/set_cookie", views.set_cookie), 11 | URLPattern("/login", views.login), 12 | URLPattern("/welcome", views.welcome), 13 | ] 14 | -------------------------------------------------------------------------------- /codes/chapter20-3/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | cookies: dict 7 | body: bytes 8 | params: dict 9 | 10 | def __init__( 11 | self, 12 | path: str = "", 13 | method: str = "", 14 | http_version: str = "", 15 | headers: dict = None, 16 | cookies: dict = None, 17 | body: bytes = b"", 18 | params: dict = None, 19 | ): 20 | if headers is None: 21 | headers = {} 22 | if cookies is None: 23 | cookies = {} 24 | if params is None: 25 | params = {} 26 | 27 | self.path = path 28 | self.method = method 29 | self.http_version = http_version 30 | self.headers = headers 31 | self.cookies = cookies 32 | self.body = body 33 | self.params = params 34 | -------------------------------------------------------------------------------- /codes/chapter20-3/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Union, Dict 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | headers: dict 7 | cookies: dict 8 | content_type: Optional[str] 9 | body: Union[bytes, str] 10 | 11 | def __init__( 12 | self, 13 | status_code: int = 200, 14 | headers: dict = None, 15 | cookies: dict = None, 16 | content_type: str = None, 17 | body: Union[bytes, str] = b"", 18 | ): 19 | if headers is None: 20 | headers = {} 21 | if cookies is None: 22 | cookies = {} 23 | 24 | self.status_code = status_code 25 | self.headers = headers 26 | self.cookies = cookies 27 | self.content_type = content_type 28 | self.body = body 29 | -------------------------------------------------------------------------------- /codes/chapter20-3/henango/server/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from henango.server.worker import Worker 4 | 5 | 6 | class Server: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = Worker(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter20-3/henango/template/renderer.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import settings 4 | 5 | 6 | def render(template_name: str, context): 7 | template_path = os.path.join(settings.TEMPLATES_DIR, template_name) 8 | 9 | with open(template_path) as f: 10 | template = f.read() 11 | 12 | return template.format(**context) 13 | -------------------------------------------------------------------------------- /codes/chapter20-3/henango/urls/pattern.py: -------------------------------------------------------------------------------- 1 | import re 2 | from re import Match 3 | from typing import Callable, Optional 4 | 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | class URLPattern: 10 | pattern: str 11 | view: Callable[[HTTPRequest], HTTPResponse] 12 | 13 | def __init__(self, pattern: str, view: Callable[[HTTPRequest], HTTPResponse]): 14 | self.pattern = pattern 15 | self.view = view 16 | 17 | def match(self, path: str) -> Optional[Match]: 18 | """ 19 | pathがURLパターンにマッチするか判定する 20 | マッチした場合はMatchオブジェクトを返し、マッチしなかった場合はNoneを返す 21 | """ 22 | # URLパターンを正規表現パターンに変換する 23 | # ex) '/user//profile' => '/user/(?P[^/]+)/profile' 24 | pattern = re.sub(r"<(.+?)>", r"(?P<\1>[^/]+)", self.pattern) 25 | return re.match(pattern, path) 26 | -------------------------------------------------------------------------------- /codes/chapter20-3/henango/urls/resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | from henango.http.request import HTTPRequest 4 | from henango.http.response import HTTPResponse 5 | from henango.views.static import static 6 | from urls import url_patterns 7 | 8 | 9 | class URLResolver: 10 | def resolve(self, request: HTTPRequest) -> Callable[[HTTPRequest], HTTPResponse]: 11 | """ 12 | URL解決を行う 13 | pathにマッチするURLパターンが存在した場合は、対応するviewを返す 14 | 存在しなかった場合は、static viewを返す 15 | """ 16 | for url_pattern in url_patterns: 17 | match = url_pattern.match(request.path) 18 | if match: 19 | request.params.update(match.groupdict()) 20 | return url_pattern.view 21 | 22 | return static 23 | -------------------------------------------------------------------------------- /codes/chapter20-3/henango/views/static.py: -------------------------------------------------------------------------------- 1 | import os 2 | import traceback 3 | 4 | import settings 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | def static(request: HTTPRequest) -> HTTPResponse: 10 | """ 11 | 静的ファイルからレスポンスを取得する 12 | """ 13 | 14 | try: 15 | static_root = getattr(settings, "STATIC_ROOT") 16 | 17 | # pathの先頭の/を削除し、相対パスにしておく 18 | relative_path = request.path.lstrip("/") 19 | # ファイルのpathを取得 20 | static_file_path = os.path.join(static_root, relative_path) 21 | 22 | with open(static_file_path, "rb") as f: 23 | response_body = f.read() 24 | 25 | content_type = None 26 | return HTTPResponse(body=response_body, content_type=content_type, status_code=200) 27 | 28 | except OSError: 29 | # ファイルを取得できなかった場合は、ログを出力して404を返す 30 | traceback.print_exc() 31 | 32 | response_body = b"

404 Not Found

" 33 | content_type = "text/html;" 34 | return HTTPResponse(body=response_body, content_type=content_type, status_code=404) 35 | -------------------------------------------------------------------------------- /codes/chapter20-3/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # 実行ファイルのあるディレクトリ 4 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | 6 | # 静的配信するファイルを置くディレクトリ 7 | STATIC_ROOT = os.path.join(BASE_DIR, "static") 8 | 9 | # テンプレートファイルを置くディレクトリ 10 | TEMPLATES_DIR = os.path.join(BASE_DIR, "templates") 11 | -------------------------------------------------------------------------------- /codes/chapter20-3/start.py: -------------------------------------------------------------------------------- 1 | from henango.server.server import Server 2 | 3 | if __name__ == "__main__": 4 | Server().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter20-3/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter20-3/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter20-3/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter20-3/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter20-3/static/logo.png -------------------------------------------------------------------------------- /codes/chapter20-3/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

【ログイン】名前を入力してください

4 | 5 |
6 | 名前:
7 | メールアドレス:
8 | 9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /codes/chapter20-3/templates/now.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Now: {now}

4 | 5 | -------------------------------------------------------------------------------- /codes/chapter20-3/templates/parameters.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Parameters:

4 |
{params}
5 | 6 | -------------------------------------------------------------------------------- /codes/chapter20-3/templates/show_request.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Request Line:

4 |

5 | {request.method} {request.path} {request.http_version} 6 |

7 |

Headers:

8 |
{headers}
9 |

Body:

10 |
{body}
11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter20-3/templates/user_profile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

プロフィール

4 |

ID: {user_id} 5 | 6 | -------------------------------------------------------------------------------- /codes/chapter20-3/templates/welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

ようこそ! {username} さん!

4 |

あなたのメールアドレスは {email} です。

5 | 6 | -------------------------------------------------------------------------------- /codes/chapter20-3/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | from henango.urls.pattern import URLPattern 3 | 4 | # pathとview関数の対応 5 | url_patterns = [ 6 | URLPattern("/now", views.now), 7 | URLPattern("/show_request", views.show_request), 8 | URLPattern("/parameters", views.parameters), 9 | URLPattern("/user//profile", views.user_profile), 10 | URLPattern("/set_cookie", views.set_cookie), 11 | URLPattern("/login", views.login), 12 | URLPattern("/welcome", views.welcome), 13 | ] 14 | -------------------------------------------------------------------------------- /codes/chapter20-4/henango/http/cookie.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import Optional 3 | 4 | 5 | class Cookie: 6 | name: str 7 | value: str 8 | expires: Optional[datetime] 9 | max_age: Optional[int] 10 | domain: str 11 | path: str 12 | secure: bool 13 | http_only: bool 14 | 15 | def __init__( 16 | self, 17 | name: str, 18 | value: str, 19 | expires: datetime = None, 20 | max_age: int = None, 21 | domain: str = "", 22 | path: str = "", 23 | secure: bool = False, 24 | http_only: bool = False, 25 | ): 26 | self.name = name 27 | self.value = value 28 | self.expires = expires 29 | self.max_age = max_age 30 | self.domain = domain 31 | self.path = path 32 | self.secure = secure 33 | self.http_only = http_only 34 | -------------------------------------------------------------------------------- /codes/chapter20-4/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | cookies: dict 7 | body: bytes 8 | params: dict 9 | 10 | def __init__( 11 | self, 12 | path: str = "", 13 | method: str = "", 14 | http_version: str = "", 15 | headers: dict = None, 16 | cookies: dict = None, 17 | body: bytes = b"", 18 | params: dict = None, 19 | ): 20 | if headers is None: 21 | headers = {} 22 | if cookies is None: 23 | cookies = {} 24 | if params is None: 25 | params = {} 26 | 27 | self.path = path 28 | self.method = method 29 | self.http_version = http_version 30 | self.headers = headers 31 | self.cookies = cookies 32 | self.body = body 33 | self.params = params 34 | -------------------------------------------------------------------------------- /codes/chapter20-4/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Union, List 2 | 3 | from henango.http.cookie import Cookie 4 | 5 | 6 | class HTTPResponse: 7 | status_code: int 8 | headers: dict 9 | cookies: List[Cookie] 10 | content_type: Optional[str] 11 | body: Union[bytes, str] 12 | 13 | def __init__( 14 | self, 15 | status_code: int = 200, 16 | headers: dict = None, 17 | cookies: List[Cookie] = None, 18 | content_type: str = None, 19 | body: Union[bytes, str] = b"", 20 | ): 21 | if headers is None: 22 | headers = {} 23 | if cookies is None: 24 | cookies = [] 25 | 26 | self.status_code = status_code 27 | self.headers = headers 28 | self.cookies = cookies 29 | self.content_type = content_type 30 | self.body = body 31 | -------------------------------------------------------------------------------- /codes/chapter20-4/henango/server/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from henango.server.worker import Worker 4 | 5 | 6 | class Server: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = Worker(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter20-4/henango/template/renderer.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import settings 4 | 5 | 6 | def render(template_name: str, context): 7 | template_path = os.path.join(settings.TEMPLATES_DIR, template_name) 8 | 9 | with open(template_path) as f: 10 | template = f.read() 11 | 12 | return template.format(**context) 13 | -------------------------------------------------------------------------------- /codes/chapter20-4/henango/urls/pattern.py: -------------------------------------------------------------------------------- 1 | import re 2 | from re import Match 3 | from typing import Callable, Optional 4 | 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | class URLPattern: 10 | pattern: str 11 | view: Callable[[HTTPRequest], HTTPResponse] 12 | 13 | def __init__(self, pattern: str, view: Callable[[HTTPRequest], HTTPResponse]): 14 | self.pattern = pattern 15 | self.view = view 16 | 17 | def match(self, path: str) -> Optional[Match]: 18 | """ 19 | pathがURLパターンにマッチするか判定する 20 | マッチした場合はMatchオブジェクトを返し、マッチしなかった場合はNoneを返す 21 | """ 22 | # URLパターンを正規表現パターンに変換する 23 | # ex) '/user//profile' => '/user/(?P[^/]+)/profile' 24 | pattern = re.sub(r"<(.+?)>", r"(?P<\1>[^/]+)", self.pattern) 25 | return re.match(pattern, path) 26 | -------------------------------------------------------------------------------- /codes/chapter20-4/henango/urls/resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | from henango.http.request import HTTPRequest 4 | from henango.http.response import HTTPResponse 5 | from henango.views.static import static 6 | from urls import url_patterns 7 | 8 | 9 | class URLResolver: 10 | def resolve(self, request: HTTPRequest) -> Callable[[HTTPRequest], HTTPResponse]: 11 | """ 12 | URL解決を行う 13 | pathにマッチするURLパターンが存在した場合は、対応するviewを返す 14 | 存在しなかった場合は、static viewを返す 15 | """ 16 | for url_pattern in url_patterns: 17 | match = url_pattern.match(request.path) 18 | if match: 19 | request.params.update(match.groupdict()) 20 | return url_pattern.view 21 | 22 | return static 23 | -------------------------------------------------------------------------------- /codes/chapter20-4/henango/views/static.py: -------------------------------------------------------------------------------- 1 | import os 2 | import traceback 3 | 4 | import settings 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | def static(request: HTTPRequest) -> HTTPResponse: 10 | """ 11 | 静的ファイルからレスポンスを取得する 12 | """ 13 | 14 | try: 15 | static_root = getattr(settings, "STATIC_ROOT") 16 | 17 | # pathの先頭の/を削除し、相対パスにしておく 18 | relative_path = request.path.lstrip("/") 19 | # ファイルのpathを取得 20 | static_file_path = os.path.join(static_root, relative_path) 21 | 22 | with open(static_file_path, "rb") as f: 23 | response_body = f.read() 24 | 25 | content_type = None 26 | return HTTPResponse(body=response_body, content_type=content_type, status_code=200) 27 | 28 | except OSError: 29 | # ファイルを取得できなかった場合は、ログを出力して404を返す 30 | traceback.print_exc() 31 | 32 | response_body = b"

404 Not Found

" 33 | content_type = "text/html;" 34 | return HTTPResponse(body=response_body, content_type=content_type, status_code=404) 35 | -------------------------------------------------------------------------------- /codes/chapter20-4/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # 実行ファイルのあるディレクトリ 4 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | 6 | # 静的配信するファイルを置くディレクトリ 7 | STATIC_ROOT = os.path.join(BASE_DIR, "static") 8 | 9 | # テンプレートファイルを置くディレクトリ 10 | TEMPLATES_DIR = os.path.join(BASE_DIR, "templates") 11 | -------------------------------------------------------------------------------- /codes/chapter20-4/start.py: -------------------------------------------------------------------------------- 1 | from henango.server.server import Server 2 | 3 | if __name__ == "__main__": 4 | Server().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter20-4/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter20-4/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter20-4/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter20-4/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter20-4/static/logo.png -------------------------------------------------------------------------------- /codes/chapter20-4/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

【ログイン】名前を入力してください

4 | 5 |
6 | 名前:
7 | メールアドレス:
8 | 9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /codes/chapter20-4/templates/now.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Now: {now}

4 | 5 | -------------------------------------------------------------------------------- /codes/chapter20-4/templates/parameters.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Parameters:

4 |
{params}
5 | 6 | -------------------------------------------------------------------------------- /codes/chapter20-4/templates/show_request.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Request Line:

4 |

5 | {request.method} {request.path} {request.http_version} 6 |

7 |

Headers:

8 |
{headers}
9 |

Body:

10 |
{body}
11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter20-4/templates/user_profile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

プロフィール

4 |

ID: {user_id} 5 | 6 | -------------------------------------------------------------------------------- /codes/chapter20-4/templates/welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

ようこそ! {username} さん!

4 |

あなたのメールアドレスは {email} です。

5 | 6 | -------------------------------------------------------------------------------- /codes/chapter20-4/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | from henango.urls.pattern import URLPattern 3 | 4 | # pathとview関数の対応 5 | url_patterns = [ 6 | URLPattern("/now", views.now), 7 | URLPattern("/show_request", views.show_request), 8 | URLPattern("/parameters", views.parameters), 9 | URLPattern("/user//profile", views.user_profile), 10 | URLPattern("/set_cookie", views.set_cookie), 11 | URLPattern("/login", views.login), 12 | URLPattern("/welcome", views.welcome), 13 | ] 14 | -------------------------------------------------------------------------------- /codes/chapter20/henango/http/request.py: -------------------------------------------------------------------------------- 1 | class HTTPRequest: 2 | path: str 3 | method: str 4 | http_version: str 5 | headers: dict 6 | body: bytes 7 | params: dict 8 | 9 | def __init__( 10 | self, 11 | path: str = "", 12 | method: str = "", 13 | http_version: str = "", 14 | headers: dict = None, 15 | body: bytes = b"", 16 | params: dict = None, 17 | ): 18 | if headers is None: 19 | headers = {} 20 | if params is None: 21 | params = {} 22 | 23 | self.path = path 24 | self.method = method 25 | self.http_version = http_version 26 | self.headers = headers 27 | self.body = body 28 | self.params = params 29 | -------------------------------------------------------------------------------- /codes/chapter20/henango/http/response.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Union, Dict 2 | 3 | 4 | class HTTPResponse: 5 | status_code: int 6 | headers: dict 7 | content_type: Optional[str] 8 | body: Union[bytes, str] 9 | 10 | def __init__( 11 | self, 12 | status_code: int = 200, 13 | headers: dict = None, 14 | content_type: str = None, 15 | body: Union[bytes, str] = b"", 16 | ): 17 | if headers is None: 18 | headers = {} 19 | 20 | self.status_code = status_code 21 | self.headers = headers 22 | self.content_type = content_type 23 | self.body = body 24 | -------------------------------------------------------------------------------- /codes/chapter20/henango/server/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from henango.server.worker import Worker 4 | 5 | 6 | class Server: 7 | """ 8 | Webサーバーを表すクラス 9 | """ 10 | 11 | def serve(self): 12 | """ 13 | サーバーを起動する 14 | """ 15 | 16 | print("=== Server: サーバーを起動します ===") 17 | 18 | try: 19 | # socketを生成 20 | server_socket = self.create_server_socket() 21 | 22 | while True: 23 | # 外部からの接続を待ち、接続があったらコネクションを確立する 24 | print("=== Server: クライアントからの接続を待ちます ===") 25 | (client_socket, address) = server_socket.accept() 26 | print(f"=== Server: クライアントとの接続が完了しました remote_address: {address} ===") 27 | 28 | # クライアントを処理するスレッドを作成 29 | thread = Worker(client_socket, address) 30 | # スレッドを実行 31 | thread.start() 32 | 33 | finally: 34 | print("=== Server: サーバーを停止します。 ===") 35 | 36 | def create_server_socket(self) -> socket: 37 | """ 38 | 通信を待ち受けるためのserver_socketを生成する 39 | :return: 40 | """ 41 | # socketの生成 42 | server_socket = socket.socket() 43 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 44 | 45 | # socketをlocalhostのポート8080番に割り当てる 46 | server_socket.bind(("localhost", 8080)) 47 | server_socket.listen(10) 48 | return server_socket 49 | -------------------------------------------------------------------------------- /codes/chapter20/henango/template/renderer.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import settings 4 | 5 | 6 | def render(template_name: str, context): 7 | template_path = os.path.join(settings.TEMPLATES_DIR, template_name) 8 | 9 | with open(template_path) as f: 10 | template = f.read() 11 | 12 | return template.format(**context) 13 | -------------------------------------------------------------------------------- /codes/chapter20/henango/urls/pattern.py: -------------------------------------------------------------------------------- 1 | import re 2 | from re import Match 3 | from typing import Callable, Optional 4 | 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | class URLPattern: 10 | pattern: str 11 | view: Callable[[HTTPRequest], HTTPResponse] 12 | 13 | def __init__(self, pattern: str, view: Callable[[HTTPRequest], HTTPResponse]): 14 | self.pattern = pattern 15 | self.view = view 16 | 17 | def match(self, path: str) -> Optional[Match]: 18 | """ 19 | pathがURLパターンにマッチするか判定する 20 | マッチした場合はMatchオブジェクトを返し、マッチしなかった場合はNoneを返す 21 | """ 22 | # URLパターンを正規表現パターンに変換する 23 | # ex) '/user//profile' => '/user/(?P[^/]+)/profile' 24 | pattern = re.sub(r"<(.+?)>", r"(?P<\1>[^/]+)", self.pattern) 25 | return re.match(pattern, path) 26 | -------------------------------------------------------------------------------- /codes/chapter20/henango/urls/resolver.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | from henango.http.request import HTTPRequest 4 | from henango.http.response import HTTPResponse 5 | from henango.views.static import static 6 | from urls import url_patterns 7 | 8 | 9 | class URLResolver: 10 | def resolve(self, request: HTTPRequest) -> Callable[[HTTPRequest], HTTPResponse]: 11 | """ 12 | URL解決を行う 13 | pathにマッチするURLパターンが存在した場合は、対応するviewを返す 14 | 存在しなかった場合は、static viewを返す 15 | """ 16 | for url_pattern in url_patterns: 17 | match = url_pattern.match(request.path) 18 | if match: 19 | request.params.update(match.groupdict()) 20 | return url_pattern.view 21 | 22 | return static 23 | -------------------------------------------------------------------------------- /codes/chapter20/henango/views/static.py: -------------------------------------------------------------------------------- 1 | import os 2 | import traceback 3 | 4 | import settings 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | 8 | 9 | def static(request: HTTPRequest) -> HTTPResponse: 10 | """ 11 | 静的ファイルからレスポンスを取得する 12 | """ 13 | 14 | try: 15 | static_root = getattr(settings, "STATIC_ROOT") 16 | 17 | # pathの先頭の/を削除し、相対パスにしておく 18 | relative_path = request.path.lstrip("/") 19 | # ファイルのpathを取得 20 | static_file_path = os.path.join(static_root, relative_path) 21 | 22 | with open(static_file_path, "rb") as f: 23 | response_body = f.read() 24 | 25 | content_type = None 26 | return HTTPResponse(body=response_body, content_type=content_type, status_code=200) 27 | 28 | except OSError: 29 | # ファイルを取得できなかった場合は、ログを出力して404を返す 30 | traceback.print_exc() 31 | 32 | response_body = b"

404 Not Found

" 33 | content_type = "text/html;" 34 | return HTTPResponse(body=response_body, content_type=content_type, status_code=404) 35 | -------------------------------------------------------------------------------- /codes/chapter20/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # 実行ファイルのあるディレクトリ 4 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | 6 | # 静的配信するファイルを置くディレクトリ 7 | STATIC_ROOT = os.path.join(BASE_DIR, "static") 8 | 9 | # テンプレートファイルを置くディレクトリ 10 | TEMPLATES_DIR = os.path.join(BASE_DIR, "templates") 11 | -------------------------------------------------------------------------------- /codes/chapter20/start.py: -------------------------------------------------------------------------------- 1 | from henango.server.server import Server 2 | 3 | if __name__ == "__main__": 4 | Server().serve() 5 | -------------------------------------------------------------------------------- /codes/chapter20/static/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | テキストボックス:
5 | パスワード:
6 | テキストエリア:
7 | 8 | 9 | 10 | 17 | 18 | 19 | 26 | 27 |
11 | ラジオボタン:
12 | 1 13 | 2 14 | 3 15 | 4 16 |
20 | チェックボックス:
21 | 1 22 | 2 23 | 3 24 | 4 25 |
28 | 29 | ファイルアップロード:
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /codes/chapter20/static/index.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /codes/chapter20/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HenaServer 6 | 7 | 8 | 9 | logo 10 |

Welcome to HenaServer!

11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter20/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigen1925/introduction-to-web-application-with-python/c268ae60615abe7d659d3d1465802a90287fc5f6/codes/chapter20/static/logo.png -------------------------------------------------------------------------------- /codes/chapter20/templates/now.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Now: {now}

4 | 5 | -------------------------------------------------------------------------------- /codes/chapter20/templates/parameters.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Parameters:

4 |
{params}
5 | 6 | -------------------------------------------------------------------------------- /codes/chapter20/templates/show_request.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Request Line:

4 |

5 | {request.method} {request.path} {request.http_version} 6 |

7 |

Headers:

8 |
{headers}
9 |

Body:

10 |
{body}
11 | 12 | 13 | -------------------------------------------------------------------------------- /codes/chapter20/templates/user_profile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

プロフィール

4 |

ID: {user_id} 5 | 6 | -------------------------------------------------------------------------------- /codes/chapter20/urls.py: -------------------------------------------------------------------------------- 1 | import views 2 | from henango.urls.pattern import URLPattern 3 | 4 | # pathとview関数の対応 5 | url_patterns = [ 6 | URLPattern("/now", views.now), 7 | URLPattern("/show_request", views.show_request), 8 | URLPattern("/parameters", views.parameters), 9 | URLPattern("/user//profile", views.user_profile), 10 | URLPattern("/set_cookie", views.set_cookie) 11 | ] 12 | -------------------------------------------------------------------------------- /codes/chapter20/views.py: -------------------------------------------------------------------------------- 1 | import urllib.parse 2 | from datetime import datetime 3 | from pprint import pformat 4 | 5 | from henango.http.request import HTTPRequest 6 | from henango.http.response import HTTPResponse 7 | from henango.template.renderer import render 8 | 9 | 10 | def now(request: HTTPRequest) -> HTTPResponse: 11 | """ 12 | 現在時刻を表示するHTMLを生成する 13 | """ 14 | context = {"now": datetime.now()} 15 | body = render("now.html", context) 16 | 17 | return HTTPResponse(body=body) 18 | 19 | 20 | def show_request(request: HTTPRequest) -> HTTPResponse: 21 | """ 22 | HTTPリクエストの内容を表示するHTMLを生成する 23 | """ 24 | context = {"request": request, "headers": pformat(request.headers), "body": request.body.decode("utf-8", "ignore")} 25 | body = render("show_request.html", context) 26 | 27 | return HTTPResponse(body=body) 28 | 29 | 30 | def parameters(request: HTTPRequest) -> HTTPResponse: 31 | """ 32 | POSTパラメータを表示するHTMLを表示する 33 | """ 34 | 35 | # GETリクエストの場合は、405を返す 36 | if request.method == "GET": 37 | body = b"

405 Method Not Allowed

" 38 | 39 | return HTTPResponse(body=body, status_code=405) 40 | 41 | elif request.method == "POST": 42 | context = {"params": urllib.parse.parse_qs(request.body.decode())} 43 | body = render("parameters.html", context) 44 | 45 | return HTTPResponse(body=body) 46 | 47 | 48 | def user_profile(request: HTTPRequest) -> HTTPResponse: 49 | context = {"user_id": request.params["user_id"]} 50 | 51 | body = render("user_profile.html", context) 52 | 53 | return HTTPResponse(body=body) 54 | 55 | 56 | def set_cookie(request: HTTPRequest) -> HTTPResponse: 57 | return HTTPResponse(headers={"Set-Cookie": "username=TARO"}) 58 | -------------------------------------------------------------------------------- /codes/chapter6/tcpserver.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | 4 | class TCPServer: 5 | """ 6 | TCP通信を行うサーバーを表すクラス 7 | """ 8 | 9 | def serve(self): 10 | """ 11 | サーバーを起動する 12 | """ 13 | 14 | print("=== サーバーを起動します ===") 15 | 16 | try: 17 | # socketを生成 18 | server_socket = socket.socket() 19 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 20 | 21 | # socketをlocalhostのポート8080番に割り当てる 22 | server_socket.bind(("localhost", 8080)) 23 | server_socket.listen(10) 24 | 25 | # 外部からの接続を待ち、接続があったらコネクションを確立する 26 | print("=== クライアントからの接続を待ちます ===") 27 | (client_socket, address) = server_socket.accept() 28 | print(f"=== クライアントとの接続が完了しました remote_address: {address} ===") 29 | 30 | # クライアントから送られてきたデータを取得する 31 | request = client_socket.recv(4096) 32 | 33 | # クライアントから送られてきたデータをファイルに書き出す 34 | with open("server_recv.txt", "wb") as f: 35 | f.write(request) 36 | 37 | # 返事は特に返さず、通信を終了させる 38 | client_socket.close() 39 | 40 | finally: 41 | print("=== サーバーを停止します。 ===") 42 | 43 | 44 | if __name__ == "__main__": 45 | server = TCPServer() 46 | server.serve() 47 | -------------------------------------------------------------------------------- /codes/chapter7/client_send.txt: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host: localhost:8080 3 | Connection: keep-alive 4 | Upgrade-Insecure-Requests: 1 5 | User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36 6 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 7 | Sec-Fetch-Site: none 8 | Sec-Fetch-Mode: navigate 9 | Sec-Fetch-User: ?1 10 | Sec-Fetch-Dest: document 11 | Accept-Encoding: gzip, deflate, br 12 | Accept-Language: ja-JP,ja;q=0.9,en-US;q=0.8,en;q=0.7 13 | 14 | -------------------------------------------------------------------------------- /codes/chapter7/tcpclient.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | 4 | class TCPClient: 5 | """ 6 | TCP通信を行うクライアントを表すクラス 7 | """ 8 | 9 | def request(self): 10 | """ 11 | サーバーへリクエストを送信する 12 | """ 13 | 14 | print("=== クライアントを起動します ===") 15 | 16 | try: 17 | # socketを生成 18 | client_socket = socket.socket() 19 | client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 20 | 21 | # サーバーと接続する 22 | print("=== サーバーと接続します ===") 23 | client_socket.connect(("localhost", 80)) 24 | print("=== サーバーとの接続が完了しました ===") 25 | 26 | # サーバーに送信するリクエストを、ファイルから取得する 27 | with open("client_send.txt", "rb") as f: 28 | request = f.read() 29 | 30 | # サーバーへリクエストを送信する 31 | client_socket.send(request) 32 | 33 | # サーバーからレスポンスが送られてくるのを待ち、取得する 34 | response = client_socket.recv(4096) 35 | 36 | # レスポンスの内容を、ファイルに書き出す 37 | with open("client_recv.txt", "wb") as f: 38 | f.write(response) 39 | 40 | # 通信を終了させる 41 | client_socket.close() 42 | 43 | finally: 44 | print("=== クライアントを停止します。 ===") 45 | 46 | 47 | if __name__ == "__main__": 48 | client = TCPClient() 49 | client.request() 50 | -------------------------------------------------------------------------------- /codes/chapter8/server_send.txt: -------------------------------------------------------------------------------- 1 | HTTP/1.1 200 OK 2 | Date: Wed, 28 Oct 2020 07:57:45 GMT 3 | Server: Apache/2.4.41 (Unix) 4 | Content-Location: index.html.en 5 | Vary: negotiate 6 | TCN: choice 7 | Last-Modified: Thu, 29 Aug 2019 05:05:59 GMT 8 | ETag: "2d-5913a76187bc0" 9 | Accept-Ranges: bytes 10 | Content-Length: 45 11 | Keep-Alive: timeout=5, max=100 12 | Connection: Keep-Alive 13 | Content-Type: text/html 14 | 15 |

It works!

16 | -------------------------------------------------------------------------------- /codes/chapter8/tcpserver.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | 4 | class TCPServer: 5 | """ 6 | TCP通信を行うサーバーを表すクラス 7 | """ 8 | 9 | def serve(self): 10 | """ 11 | サーバーを起動する 12 | """ 13 | 14 | print("=== サーバーを起動します ===") 15 | 16 | try: 17 | # socketを生成 18 | server_socket = socket.socket() 19 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 20 | 21 | # socketをlocalhostのポート8080番に割り当てる 22 | server_socket.bind(("localhost", 8080)) 23 | server_socket.listen(10) 24 | 25 | # 外部からの接続を待ち、接続があったらコネクションを確立する 26 | print("=== クライアントからの接続を待ちます ===") 27 | (client_socket, address) = server_socket.accept() 28 | print(f"=== クライアントとの接続が完了しました remote_address: {address} ===") 29 | 30 | # クライアントから送られてきたデータを取得する 31 | request = client_socket.recv(4096) 32 | 33 | # クライアントから送られてきたデータをファイルに書き出す 34 | with open("server_recv.txt", "wb") as f: 35 | f.write(request) 36 | 37 | # クライアントへ送信するレスポンスデータをファイルから取得する 38 | with open("server_send.txt", "rb") as f: 39 | response = f.read() 40 | 41 | # クライアントへレスポンスを送信する 42 | client_socket.send(response) 43 | 44 | # 通信を終了させる 45 | client_socket.close() 46 | 47 | finally: 48 | print("=== サーバーを停止します。 ===") 49 | 50 | 51 | if __name__ == "__main__": 52 | server = TCPServer() 53 | server.serve() 54 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "python_web_application_for_3rd_year_engineer", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "python_web_application_for_3rd_year_engineer", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "zenn-cli": "^0.1.134" 13 | } 14 | }, 15 | "node_modules/zenn-cli": { 16 | "version": "0.1.134", 17 | "resolved": "https://registry.npmjs.org/zenn-cli/-/zenn-cli-0.1.134.tgz", 18 | "integrity": "sha512-DOo5MZbGQagO0S2l5jvWtTgf6+OvHjrK3v1YE4SzeNldtHJ1FV+hafxu41+yIxtG/T0iqB6N/+kl2nY7rqJUYQ==", 19 | "bin": { 20 | "zenn": "dist/server/zenn.js" 21 | }, 22 | "engines": { 23 | "node": ">=14.0.0" 24 | } 25 | } 26 | }, 27 | "dependencies": { 28 | "zenn-cli": { 29 | "version": "0.1.134", 30 | "resolved": "https://registry.npmjs.org/zenn-cli/-/zenn-cli-0.1.134.tgz", 31 | "integrity": "sha512-DOo5MZbGQagO0S2l5jvWtTgf6+OvHjrK3v1YE4SzeNldtHJ1FV+hafxu41+yIxtG/T0iqB6N/+kl2nY7rqJUYQ==" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "python_web_application_for_3rd_year_engineer", 3 | "version": "1.0.0", 4 | "description": "伸び悩んでいる3年目Webエンジニアのための、Python Webアプリケーション自作入門", 5 | "repository": { 6 | "type": "git", 7 | "url": "git@github.private.com:bigen1925/python_web_application_for_3rd_year_engineer.git" 8 | }, 9 | "author": "bigen1925@gmail.com", 10 | "license": "MIT", 11 | "dependencies": { 12 | "zenn-cli": "^0.1.134" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 120 3 | target-version = ['py38'] --------------------------------------------------------------------------------