waitress-core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/Rakefile +4 -0
- data/bin/waitress +22 -0
- data/ext/Thanks.md +1 -0
- data/ext/waitress_http11/ext_help.h +15 -0
- data/ext/waitress_http11/extconf.rb +6 -0
- data/ext/waitress_http11/http11.c +532 -0
- data/ext/waitress_http11/http11_parser.c +1216 -0
- data/ext/waitress_http11/http11_parser.h +49 -0
- data/ext/waitress_http11/http11_parser.java.rl +171 -0
- data/ext/waitress_http11/http11_parser.rl +165 -0
- data/ext/waitress_http11/http11_parser_common.rl +55 -0
- data/ext/waitress_http11/http11_wrb_parser.h +83 -0
- data/lib/waitress.rb +98 -0
- data/lib/waitress/chef.rb +110 -0
- data/lib/waitress/configure.rb +116 -0
- data/lib/waitress/handlers/dirhandler.rb +39 -0
- data/lib/waitress/handlers/handler.rb +57 -0
- data/lib/waitress/handlers/handler404.rb +25 -0
- data/lib/waitress/handlers/libhandler.rb +58 -0
- data/lib/waitress/kernel.rb +182 -0
- data/lib/waitress/parse/query.rb +60 -0
- data/lib/waitress/request.rb +45 -0
- data/lib/waitress/resources/default_config.rb +52 -0
- data/lib/waitress/resources/http/404.html +18 -0
- data/lib/waitress/resources/http/css/hack.css +37 -0
- data/lib/waitress/resources/http/css/waitress.css +57 -0
- data/lib/waitress/resources/http/fonts/eot/latin/hack-bold-latin-webfont.eot +0 -0
- data/lib/waitress/resources/http/fonts/eot/latin/hack-bolditalic-latin-webfont.eot +0 -0
- data/lib/waitress/resources/http/fonts/eot/latin/hack-italic-latin-webfont.eot +0 -0
- data/lib/waitress/resources/http/fonts/eot/latin/hack-regular-latin-webfont.eot +0 -0
- data/lib/waitress/resources/http/fonts/svg/latin/hack-bold-latin-webfont.svg +241 -0
- data/lib/waitress/resources/http/fonts/svg/latin/hack-bolditalic-latin-webfont.svg +241 -0
- data/lib/waitress/resources/http/fonts/svg/latin/hack-italic-latin-webfont.svg +241 -0
- data/lib/waitress/resources/http/fonts/svg/latin/hack-regular-latin-webfont.svg +241 -0
- data/lib/waitress/resources/http/fonts/web-ttf/latin/hack-bold-latin-webfont.ttf +0 -0
- data/lib/waitress/resources/http/fonts/web-ttf/latin/hack-bolditalic-latin-webfont.ttf +0 -0
- data/lib/waitress/resources/http/fonts/web-ttf/latin/hack-italic-latin-webfont.ttf +0 -0
- data/lib/waitress/resources/http/fonts/web-ttf/latin/hack-regular-latin-webfont.ttf +0 -0
- data/lib/waitress/resources/http/fonts/woff/latin/hack-bold-latin-webfont.woff +0 -0
- data/lib/waitress/resources/http/fonts/woff/latin/hack-bolditalic-latin-webfont.woff +0 -0
- data/lib/waitress/resources/http/fonts/woff/latin/hack-italic-latin-webfont.woff +0 -0
- data/lib/waitress/resources/http/fonts/woff/latin/hack-regular-latin-webfont.woff +0 -0
- data/lib/waitress/resources/http/fonts/woff2/latin/hack-bold-latin-webfont.woff2 +0 -0
- data/lib/waitress/resources/http/fonts/woff2/latin/hack-bolditalic-latin-webfont.woff2 +0 -0
- data/lib/waitress/resources/http/fonts/woff2/latin/hack-italic-latin-webfont.woff2 +0 -0
- data/lib/waitress/resources/http/fonts/woff2/latin/hack-regular-latin-webfont.woff2 +0 -0
- data/lib/waitress/resources/http/img/404.png +0 -0
- data/lib/waitress/resources/http/index.html +15 -0
- data/lib/waitress/response.rb +104 -0
- data/lib/waitress/server.rb +115 -0
- data/lib/waitress/util.rb +713 -0
- data/lib/waitress/version.rb +3 -0
- data/lib/waitress/vhost.rb +217 -0
- data/lib/waitress_http11.so +0 -0
- data/waitress-core.gemspec +27 -0
- metadata +187 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
module Waitress
|
2
|
+
|
3
|
+
# The LibraryHandler is used to handle requests to the VHost regarding the
|
4
|
+
# libraries to be loaded by other Handlers and .wrb files. This will take any
|
5
|
+
# requests to /libraries (or whatever the user has set it to) to load libraries
|
6
|
+
class LibraryHandler < Handler
|
7
|
+
|
8
|
+
attr_accessor :priority
|
9
|
+
|
10
|
+
def initialize libraries, libdir, liburi, vhost
|
11
|
+
@priority = 150
|
12
|
+
@vhost = vhost
|
13
|
+
@libraries, @libdir, @liburi = libraries, File.expand_path(libdir), liburi
|
14
|
+
FileUtils.mkdir_p(@libdir) unless File.exist?(@libdir)
|
15
|
+
|
16
|
+
@libraries.each do |name, lib|
|
17
|
+
l = {}
|
18
|
+
d = dirType(lib[:bindtype])
|
19
|
+
matches = Dir["#{d}/**/*.#{lib[:bindtype].to_s}"].select { |x| (x =~ lib[:pattern]) != nil }
|
20
|
+
if matches.length > 0
|
21
|
+
l[:file] = matches[0]
|
22
|
+
l[:type] = lib[:bindtype]
|
23
|
+
else
|
24
|
+
l = nil
|
25
|
+
end
|
26
|
+
@libraries[name] = l
|
27
|
+
end
|
28
|
+
|
29
|
+
[:css, :js].each do |k|
|
30
|
+
d = dirType k
|
31
|
+
FileUtils.mkdir_p(d) unless File.exist?(d)
|
32
|
+
|
33
|
+
Dir["#{d}/**/*.#{k.to_s}"].each do |fl|
|
34
|
+
@libraries[File.basename(fl).to_sym] = { :file => fl, :type => k }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def dirType k
|
40
|
+
File.join(@libdir, k.to_s)
|
41
|
+
end
|
42
|
+
|
43
|
+
def respond? request, vhost
|
44
|
+
path = request.path
|
45
|
+
return false unless path.start_with?("/#{@liburi}/")
|
46
|
+
name = path.sub("/#{@liburi}/", "").to_sym
|
47
|
+
@libraries.include?(name)
|
48
|
+
end
|
49
|
+
|
50
|
+
def serve request, response, client, vhost
|
51
|
+
path = request.path
|
52
|
+
name = path.sub("/#{@liburi}/", "").to_sym
|
53
|
+
lib = @libraries[name]
|
54
|
+
Waitress::Chef.serve_file request, response, client, vhost, lib[:file]
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
# The Kernel Module provides global methods that will provide the 'builtins' for .wrb files
|
2
|
+
# and handlers. This is used to prevent verbose access of a namespace like Waitress::Global,
|
3
|
+
# and instead provide them here. Because requests are handled in new Processes, these values
|
4
|
+
# will change in each request and will not interfere.
|
5
|
+
module ::Kernel
|
6
|
+
|
7
|
+
# The +Waitress::Response+ object being used
|
8
|
+
def response_object
|
9
|
+
$RESPONSE
|
10
|
+
end
|
11
|
+
|
12
|
+
# The +Waitress::Request+ object being used
|
13
|
+
def request_object
|
14
|
+
$REQUEST
|
15
|
+
end
|
16
|
+
|
17
|
+
# Prepare the Kernel, by linking global variables
|
18
|
+
def kernel_prepare
|
19
|
+
$METHOD = get_method
|
20
|
+
$HEADERS = get_headers
|
21
|
+
$PATH = get_path
|
22
|
+
$URI = get_uri
|
23
|
+
$BODY = get_body
|
24
|
+
end
|
25
|
+
|
26
|
+
# Automatically load a library header into this file in the form of HTML.
|
27
|
+
# This will load a library in the VHost's libs/ folder, or any lib defined in the
|
28
|
+
# VHost's config.rb file with the name given. JS libraries will be linked with the
|
29
|
+
# <script> tag, whilst css files will be linked with the <link rel="stylesheet">
|
30
|
+
# tag. This is the recommended way of handling library loading.
|
31
|
+
# Params:
|
32
|
+
# +name+:: The name of the lib (the filename), or the name of the library as bound
|
33
|
+
# in the config.rb file.
|
34
|
+
def lib name
|
35
|
+
name = name.to_sym
|
36
|
+
type = $VHOST.libraries[name][:type]
|
37
|
+
libhome = $VHOST.liburi
|
38
|
+
if type == :js
|
39
|
+
echo "<script type='text/javascript' src='/#{libhome}/#{name}'></script>"
|
40
|
+
elsif type == :css
|
41
|
+
echo "<link rel='stylesheet' href='/#{libhome}/#{name}'></link>"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Automatically load a Library Combo into this file. This will consecutively load
|
46
|
+
# all the libraries bound to the combination with the given name as defined in the
|
47
|
+
# VHost's config.rb file. This will call lib() for each of these libraries.
|
48
|
+
# Params:
|
49
|
+
# +name+:: The name of the combo to load
|
50
|
+
def combo name
|
51
|
+
name = name.to_sym
|
52
|
+
combo_arr = $VHOST.combos[name]
|
53
|
+
combo_arr.each { |n| lib(n) }
|
54
|
+
end
|
55
|
+
|
56
|
+
# Include another .wrb, .rb or any other file in the load path of the VHost into this
|
57
|
+
# file. If this file is .wrb or .rb, it will be evaluated. If it is another type of file
|
58
|
+
# (e.g. html), it will be directly echoed to the output buffer
|
59
|
+
# Params:
|
60
|
+
# +filename+:: The name of the file, relative to the loadpath
|
61
|
+
def includes filename
|
62
|
+
Waitress::Chef.include_file filename
|
63
|
+
end
|
64
|
+
|
65
|
+
# Include another .wrb, .rb or any other file in this file. If this file is
|
66
|
+
# .wrb or .rb, it will be evaluated. If it is another type of file
|
67
|
+
# (e.g. html), it will be directly echoed to the output buffer
|
68
|
+
# Params:
|
69
|
+
# +filename+:: The absolute filename of the file to load, anywhere in the filesystem
|
70
|
+
def includes_file filename
|
71
|
+
Waitress::Chef.include_absfile filename
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns a Hash of the GET query of the request. This may be an empty array
|
75
|
+
# if querystring was present
|
76
|
+
def get
|
77
|
+
request_object.get_query
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns a Hash of the POST query of the request. This may be an empty array
|
81
|
+
# if the body is not a valid querystring, or an exception raised if there was
|
82
|
+
# an error in parsing.
|
83
|
+
def post
|
84
|
+
request_object.post_query
|
85
|
+
end
|
86
|
+
|
87
|
+
# Get a header from the HTTP Request. This will fetch the header by the given
|
88
|
+
# name from the request object. Keep in mind that in requests, Headers are fully
|
89
|
+
# capitalized and any hyphens replaced with underscores (e.g. Content-Type becomes
|
90
|
+
# CONTENT_TYPE)
|
91
|
+
def get_header name
|
92
|
+
request_object.headers[name]
|
93
|
+
end
|
94
|
+
|
95
|
+
# Return the full list of headers available in the request object.
|
96
|
+
def get_headers
|
97
|
+
request_object.headers
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns the HTTP method that was used to retrieve this page (GET, POST, UPDATE,
|
101
|
+
# DELETE, PUT, etc)
|
102
|
+
def get_method
|
103
|
+
request_object.method
|
104
|
+
end
|
105
|
+
|
106
|
+
# Get the path of this request. This is after a URL rewrite, and does not contain
|
107
|
+
# a querystring. Be careful with these, as they start with a "/" and if not joined
|
108
|
+
# correctly can cause issues in the root of your filesystem (use File.join) if you
|
109
|
+
# plan to use this
|
110
|
+
def get_path
|
111
|
+
request_object.path
|
112
|
+
end
|
113
|
+
|
114
|
+
# Get the URI of this request. Unlike the path, the URI is not modified after a rewrite,
|
115
|
+
# and does contain a querystring. Use this if you want the *original* path and query
|
116
|
+
# of the request before it was rewritten
|
117
|
+
def get_uri
|
118
|
+
request_object.uri
|
119
|
+
end
|
120
|
+
|
121
|
+
# Get the request body. In most cases, this will be blank, but for POST requests it may
|
122
|
+
# contain a querystring, and for PUT and UPDATE methods it may contain other data
|
123
|
+
def get_body
|
124
|
+
request_object.body
|
125
|
+
end
|
126
|
+
|
127
|
+
# Get the querystring object as a string before it is parsed
|
128
|
+
def get_querystring
|
129
|
+
request_object.querystring
|
130
|
+
end
|
131
|
+
|
132
|
+
# Set a response header. This will be joined when writing the response with the delimiter ": "
|
133
|
+
# as in regular HTTP Protocol fashion.
|
134
|
+
# Params:
|
135
|
+
# +name+:: The name of the header, e.g. "Content-Type"
|
136
|
+
# +value+:: The value of the header, e.g. "text/html"
|
137
|
+
def set_header name, value
|
138
|
+
response_object.header name, value
|
139
|
+
end
|
140
|
+
|
141
|
+
# Set the content-type of the response. This is a shortcut to the Content-Type
|
142
|
+
# header and takes the full content-type (not fileextension) as an argument
|
143
|
+
# Params:
|
144
|
+
# +raw_type+:: The mime type of the content, e.g. "text/html"
|
145
|
+
def content_type raw_type
|
146
|
+
response_object.mime_raw raw_type
|
147
|
+
end
|
148
|
+
|
149
|
+
# Set the content-type of the response. This is a shortcut to the Content-Type
|
150
|
+
# header, and will also lookup the fileextension in the +Waitress::Util+ mime-type
|
151
|
+
# lookup.
|
152
|
+
# Params:
|
153
|
+
# +extension+:: The file extension to map to a mimetype, e.g. ".html"
|
154
|
+
def file_ext extension
|
155
|
+
response_object.mime extension
|
156
|
+
end
|
157
|
+
|
158
|
+
# Write a string to the output buffer. This will write directly to the body of the
|
159
|
+
# response, similar to what 'print()' does for STDOUT. Use this to write data to the
|
160
|
+
# output stream
|
161
|
+
def echo obj
|
162
|
+
str = obj.to_s
|
163
|
+
write str
|
164
|
+
end
|
165
|
+
|
166
|
+
# Write a string to the output buffer, followed by a newline. Similar to echo(),
|
167
|
+
# this will write to the output buffer, but also adds a "\n". This does to the
|
168
|
+
# client output as 'puts()' does to STDOUT. Use this to write data to the output
|
169
|
+
# stream
|
170
|
+
def println obj
|
171
|
+
echo(obj.to_s + "\n")
|
172
|
+
end
|
173
|
+
|
174
|
+
# Write a set of bytes directly to the output stream. Use this if you don't want
|
175
|
+
# to cast to a string as echo() and println() do.
|
176
|
+
def write bytes
|
177
|
+
r = response_object
|
178
|
+
r.body "" if r.body_io.nil?
|
179
|
+
r.body_io.write bytes
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Waitress
|
2
|
+
# A lot of this class uses methods from Rack::QueryParser in order to Parse
|
3
|
+
# a given QueryString into a Hash of key/value pairs
|
4
|
+
class QueryParser
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
DEFAULT_SEP = /[&;] */n
|
8
|
+
|
9
|
+
# Unescape a HTTP URL to regular text. e.g. %20 becomes a space (" ")
|
10
|
+
def self.unescape str, enc=Encoding::UTF_8
|
11
|
+
URI.decode_www_form_component(str, enc)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Parse the given QueryString into a hash of key/value pairs
|
15
|
+
def self.parse qs
|
16
|
+
return {} if qs.nil? || qs.empty?
|
17
|
+
results = {}
|
18
|
+
(qs || '').split(DEFAULT_SEP).each do |p|
|
19
|
+
k, v = p.split('='.freeze, 2).map! { |s| unescape(s) }
|
20
|
+
|
21
|
+
normalize results, k, v
|
22
|
+
end
|
23
|
+
results
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.normalize hash, name, v
|
27
|
+
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
|
28
|
+
k = $1 || ''
|
29
|
+
after = $' || ''
|
30
|
+
|
31
|
+
return if k.empty?
|
32
|
+
|
33
|
+
if after == ""
|
34
|
+
hash[k] = v
|
35
|
+
elsif after == "["
|
36
|
+
hash[name] = v
|
37
|
+
elsif after == "[]"
|
38
|
+
hash[k] ||= []
|
39
|
+
raise TypeError, "expected Array (got #{hash[k].class.name}) for param `#{k}'" unless hash[k].is_a?(Array)
|
40
|
+
hash[k] << v
|
41
|
+
elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
|
42
|
+
child_key = $1
|
43
|
+
hash[k] ||= []
|
44
|
+
raise TypeError, "expected Array (got #{hash[k].class.name}) for param `#{k}'" unless hash[k].is_a?(Array)
|
45
|
+
if hash[k].last.is_a?(Hash) && !hash[k].last.key?(child_key)
|
46
|
+
normalize(hash[k].last, child_key, v)
|
47
|
+
else
|
48
|
+
hash[k] << normalize({}, child_key, v)
|
49
|
+
end
|
50
|
+
else
|
51
|
+
hash[k] ||= {}
|
52
|
+
raise TypeError, "expected Hash (got #{hash[k].class.name}) for param `#{k}'" unless hash[k].is_a?(Hash)
|
53
|
+
hash[k] = normalize_params(hash[k], after, v)
|
54
|
+
end
|
55
|
+
|
56
|
+
hash
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Waitress
|
2
|
+
# The request class is used to represent a HTTP request by a client. This includes
|
3
|
+
# all the headers sent to the server by the client, request URI and those properties
|
4
|
+
# parsed by Mongrel, including the Path and QueryString. GET and POST queries are to
|
5
|
+
# be parsed on-request by the handler or .wrb file as to not waste CPU resources.
|
6
|
+
class Request
|
7
|
+
|
8
|
+
attr_accessor :method
|
9
|
+
attr_accessor :path
|
10
|
+
attr_accessor :uri
|
11
|
+
attr_accessor :querystring
|
12
|
+
attr_accessor :http_version
|
13
|
+
attr_accessor :body
|
14
|
+
attr_accessor :headers
|
15
|
+
|
16
|
+
def initialize method, path, uri, query, http_version, body, headers
|
17
|
+
@method = method
|
18
|
+
@path = Waitress::QueryParser.unescape(path)
|
19
|
+
@uri = Waitress::QueryParser.unescape(uri)
|
20
|
+
@querystring = query
|
21
|
+
@http_version = http_version
|
22
|
+
@body = body
|
23
|
+
@headers = headers
|
24
|
+
@marks = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
# The GET query for the request in the form of a hash. This is parsed on-request
|
28
|
+
def get_query
|
29
|
+
@get ||= Waitress::QueryParser.parse(@querystring)
|
30
|
+
@get
|
31
|
+
end
|
32
|
+
|
33
|
+
# The POST query for the request in the form of a hash. This is parsed on-request
|
34
|
+
def post_query
|
35
|
+
@post ||= Waitress::QueryParser.parse(@body)
|
36
|
+
@post
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
m = lambda { |a,x| x.nil? ? "" : "#{a}=#{x.inspect}" }
|
41
|
+
"#<#{self.class} method=#{@method} path=#{@path} #{m.call("query", @query)}>"
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# This is the configuration for your waitress server, accepting connections on port '2910'
|
2
|
+
Waitress.configure!(2910) do |w|
|
3
|
+
|
4
|
+
# You can run regular Ruby Code in here, too!
|
5
|
+
@home = "~/.waitress/www/"
|
6
|
+
def fl file
|
7
|
+
File.join(@home, file)
|
8
|
+
end
|
9
|
+
|
10
|
+
# This is your main Virtual Host, accepting connections to 'example.mysite.com'
|
11
|
+
w.host(/example.mysite.com/) do |host|
|
12
|
+
# This is where your public documents and pages are stored
|
13
|
+
host.root fl("example/http")
|
14
|
+
|
15
|
+
# This is where your non-public includes are stored
|
16
|
+
host.includes fl("example/rb")
|
17
|
+
|
18
|
+
# This will show up when a 404 error occurs
|
19
|
+
# host.set_404 fl("example/http/404.html")
|
20
|
+
|
21
|
+
# Want to rewrite a URL? No problem.
|
22
|
+
host.rewrite /some_url/, "some_other_url"
|
23
|
+
host.rewrite /capture_group_(\d)/, "capture/group\\1"
|
24
|
+
|
25
|
+
# Bind_Lib will allow you to automatically import lib tags into .wrb files
|
26
|
+
# Before they are sent to the client.
|
27
|
+
host.bind_lib /jquery.*/, :js, "jquery"
|
28
|
+
host.bind_lib /boostrap.*/, :css, "boots"
|
29
|
+
|
30
|
+
# Use combos to chain together libraries into a collection
|
31
|
+
host.combo "all-lib", "jquery", "boots"
|
32
|
+
|
33
|
+
# Change this to change where you store your libraries
|
34
|
+
host.libdir fl("libs")
|
35
|
+
|
36
|
+
# Change this to choose what URI the libraries are linked
|
37
|
+
# to (yoursite.com/libraries/library_name)
|
38
|
+
host.liburi "libraries"
|
39
|
+
end
|
40
|
+
|
41
|
+
# This is your other Virtual Host, accepting connections to any domain.
|
42
|
+
# The '0' represents the priority, with the default being 50. Higher priority
|
43
|
+
# Hosts will be chosen if the request matches multiple Hosts.
|
44
|
+
w.host(/.*/, 0) do |host|
|
45
|
+
# This is where your public documents and pages are stored
|
46
|
+
host.root fl("main/http")
|
47
|
+
|
48
|
+
# This is where your non-public includes are stored
|
49
|
+
host.includes fl("main/rb")
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title> 404 - Page Not Found </title>
|
4
|
+
<link rel="stylesheet" href="/css/hack.css">
|
5
|
+
<link rel="stylesheet" href="/css/waitress.css">
|
6
|
+
</head>
|
7
|
+
<body>
|
8
|
+
<div id="maincontainer">
|
9
|
+
<div class="textcontainer">
|
10
|
+
<h1> 404 </h1>
|
11
|
+
<h2> You ordered something that wasn't on the menu </h2>
|
12
|
+
</div>
|
13
|
+
<div class="imgcontainer">
|
14
|
+
<img src="/img/404.png">
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
</body>
|
18
|
+
</html>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
/*!
|
2
|
+
* Hack v2.013 - https://sourcefoundry.org/hack/
|
3
|
+
* Licenses - Fonts: Hack Open Font License + Bitstream Vera license, CSS: MIT License
|
4
|
+
*/
|
5
|
+
/* FONT PATHS
|
6
|
+
* -------------------------- */
|
7
|
+
@font-face {
|
8
|
+
font-family: 'Hack';
|
9
|
+
src: url('../fonts/eot/latin/hack-regular-latin-webfont.eot?v=2.013');
|
10
|
+
src: url('../fonts/eot/latin/hack-regular-latin-webfont.eot?#iefix&v=2.013') format('embedded-opentype'), url('../fonts/woff2/latin/hack-regular-latin-webfont.woff2?v=2.013') format('woff2'), url('../fonts/woff/latin/hack-regular-latin-webfont.woff?v=2.013') format('woff'), url('../fonts/web-ttf/latin/hack-regular-latin-webfont.ttf?v=2.013') format('truetype'), url('../fonts/svg/latin/hack-regular-latin-webfont.svg?v=2.013#hackregular') format('svg');
|
11
|
+
font-weight: 400;
|
12
|
+
font-style: normal;
|
13
|
+
}
|
14
|
+
|
15
|
+
@font-face {
|
16
|
+
font-family: 'Hack';
|
17
|
+
src: url('../fonts/eot/latin/hack-bold-latin-webfont.eot?v=2.013');
|
18
|
+
src: url('../fonts/eot/latin/hack-bold-latin-webfont.eot?#iefix&v=2.013') format('embedded-opentype'), url('../fonts/woff2/latin/hack-bold-latin-webfont.woff2?v=2.013') format('woff2'), url('../fonts/woff/latin/hack-bold-latin-webfont.woff?v=2.013') format('woff'), url('../fonts/web-ttf/latin/hack-bold-latin-webfont.ttf?v=2.013') format('truetype'), url('../fonts/svg/latin/hack-bold-latin-webfont.svg?v=2.013#hackbold') format('svg');
|
19
|
+
font-weight: 700;
|
20
|
+
font-style: normal;
|
21
|
+
}
|
22
|
+
|
23
|
+
@font-face {
|
24
|
+
font-family: 'Hack';
|
25
|
+
src: url('../fonts/eot/latin/hack-italic-latin-webfont.eot?v=2.013');
|
26
|
+
src: url('../fonts/eot/latin/hack-italic-latin-webfont.eot?#iefix&v=2.013') format('embedded-opentype'), url('../fonts/woff2/latin/hack-italic-latin-webfont.woff2?v=2.013') format('woff2'), url('../fonts/woff/latin/hack-italic-latin-webfont.woff?v=2.013') format('woff'), url('../fonts/web-ttf/latin/hack-italic-latin-webfont.ttf?v=2.013') format('truetype'), url('../fonts/svg/latin/hack-italic-latin-webfont.svg?v=2.013#hackitalic') format('svg');
|
27
|
+
font-weight: 400;
|
28
|
+
font-style: italic;
|
29
|
+
}
|
30
|
+
|
31
|
+
@font-face {
|
32
|
+
font-family: 'Hack';
|
33
|
+
src: url('../fonts/eot/latin/hack-bolditalic-latin-webfont.eot?v=2.013');
|
34
|
+
src: url('../fonts/eot/latin/hack-bolditalic-latin-webfont.eot?#iefix&v=2.013') format('embedded-opentype'), url('../fonts/woff2/latin/hack-bolditalic-latin-webfont.woff2?v=2.013') format('woff2'), url('../fonts/woff/latin/hack-bolditalic-latin-webfont.woff?v=2.013') format('woff'), url('../fonts/web-ttf/latin/hack-bolditalic-latin-webfont.ttf?v=2.013') format('truetype'), url('../fonts/svg/latin/hack-bolditalic-latin-webfont.svg?v=2.013#hackbolditalic') format('svg');
|
35
|
+
font-weight: 700;
|
36
|
+
font-style: italic;
|
37
|
+
}
|