Browse Source

Initial commit

Oz N Tiram 5 years ago
commit
51bd13b7df
10 changed files with 306 additions and 0 deletions
  1. 16 0
      Makefile
  2. 15 0
      Pipfile
  3. 54 0
      README
  4. 0 0
      catster/__init__.py
  5. 120 0
      catster/settings.py
  6. 17 0
      catster/tests.py
  7. 24 0
      catster/urls.py
  8. 29 0
      catster/views.py
  9. 16 0
      catster/wsgi.py
  10. 15 0
      manage.py

+ 16 - 0
Makefile

@@ -0,0 +1,16 @@
+
+test:
+	python manage.py test -v 2
+
+# django test server only support either ipv6 or ipv4 but not both
+server:
+	uwsgi \
+	--module=catster.wsgi:application \
+	--env DJANGO_SETTINGS_MODULE=catster.settings \
+	--vacuum \
+	--http [::]:8000 \
+	--chdir=$(PWD)
+
+#--http [::]:8000 is both ipv6 and ipv4 whereas --http 0.0.0.0:8000 is ipv4 only
+
+

+ 15 - 0
Pipfile

@@ -0,0 +1,15 @@
+[[source]]
+
+url = "https://pypi.python.org/simple"
+verify_ssl = true
+name = "pypi"
+
+
+[packages]
+
+uwsgi = "*"
+django = "==2.1.7"
+
+
+[dev-packages]
+

+ 54 - 0
README

@@ -0,0 +1,54 @@
+Catster
+-------
+
+A simple django application to that shows cute cats purring only on ipv6.
+Users trying to view purring cats via ipv4 will be shown a message that the
+website only serves cats to ipv6 client.
+
+
+To view the application first install the dependencies:
+
+```
+make install-deps
+```
+
+To run application:
+
+```
+make server
+```
+
+To run the tests:
+
+```
+make tests
+```
+
+## Notes:
+
+While this is solved with django mechanism. The "ipv4 only" page can simply be
+served with nginx listening on 0.0.0.0 and serving a static page, while another
+server directive can listen on ipv6 and transfer the traffic to the uwsgi server.
+
+```
+         server {
+         listen          [::]:80 ipv6only=on;
+         server_name     catster.info;
+         location / {
+             root   /var/www/localhost/;
+             index  index.html index.htm;
+             }
+        }
+
+         server {
+         listen          0.0.0.0:80;
+         server_name     catster.info;
+         location / {
+             root   /var/www/localhost/nocats;
+             index  index.html index.htm;
+             }
+        }
+```
+
+Another approach using nginx is also demonstrated in the ungleich blog ...
+https://ungleich.ch/en-us/cms/blog/2019/01/27/how-to-distinguish-ipv6-and-ipv4-traffic-with-nginx/

+ 0 - 0
catster/__init__.py


+ 120 - 0
catster/settings.py

@@ -0,0 +1,120 @@
+"""
+Django settings for catster project.
+
+Generated by 'django-admin startproject' using Django 2.1.7.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/2.1/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/2.1/ref/settings/
+"""
+
+import os
+
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'js&oj*qu+d*f)d(d2uo)8@f^fg@=1vxbtpd&3#+p-o!bb3lz=k'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = ['*']
+
+
+# Application definition
+
+INSTALLED_APPS = [
+    'django.contrib.admin',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+]
+
+MIDDLEWARE = [
+    'django.middleware.security.SecurityMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'catster.urls'
+
+TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [],
+        'APP_DIRS': True,
+        'OPTIONS': {
+            'context_processors': [
+                'django.template.context_processors.debug',
+                'django.template.context_processors.request',
+                'django.contrib.auth.context_processors.auth',
+                'django.contrib.messages.context_processors.messages',
+            ],
+        },
+    },
+]
+
+WSGI_APPLICATION = 'catster.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3',
+        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+    }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+    {
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+    },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/2.1/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/2.1/howto/static-files/
+
+STATIC_URL = '/static/'

+ 17 - 0
catster/tests.py

@@ -0,0 +1,17 @@
+from django.test import Client
+from django.test import TestCase
+
+
+class MyTests(TestCase):
+
+    def test_no_cats_here(self):
+        c = Client()
+        resp = c.get("", REMOTE_ADDR="127.0.0.1")
+
+        self.assertEqual(resp.content, b"no cats purring here, only via ipv6")
+
+    def test_purring_cats(self):
+        c = Client()
+        resp = c.get("", REMOTE_ADDR="fe80::62b6:a299:1b67:3956")
+
+        self.assertEqual(resp.content, b"purrr ...")

+ 24 - 0
catster/urls.py

@@ -0,0 +1,24 @@
+"""catster URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/2.1/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.urls import include, path
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.urls import path, re_path
+
+import catster.views
+
+urlpatterns = [
+    path('admin/', admin.site.urls),
+    re_path(r'^$', catster.views.index),
+]

+ 29 - 0
catster/views.py

@@ -0,0 +1,29 @@
+from ipaddress import ip_address
+
+
+from django.http import HttpResponse
+
+
+def get_client_ip(request):
+    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
+    if x_forwarded_for:
+        ip = x_forwarded_for.split(',')[0]
+    else:
+        ip = request.META.get('REMOTE_ADDR')
+    return ip
+
+
+def no_cats_here():
+    return HttpResponse("no cats purring here, only via ipv6")
+
+
+def cute_cat():
+    return HttpResponse("purrr ...")
+
+
+def index(request):
+    ip = get_client_ip(request)
+    if ip_address(ip).version == 4:
+        return no_cats_here()
+
+    return cute_cat()

+ 16 - 0
catster/wsgi.py

@@ -0,0 +1,16 @@
+"""
+WSGI config for catster project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'catster.settings')
+
+application = get_wsgi_application()

+ 15 - 0
manage.py

@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+import os
+import sys
+
+if __name__ == '__main__':
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'catster.settings')
+    try:
+        from django.core.management import execute_from_command_line
+    except ImportError as exc:
+        raise ImportError(
+            "Couldn't import Django. Are you sure it's installed and "
+            "available on your PYTHONPATH environment variable? Did you "
+            "forget to activate a virtual environment?"
+        ) from exc
+    execute_from_command_line(sys.argv)