iodine 0.7.56 → 0.7.58

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e2f42cc055212f19560dc49734f4ab9b4e0be6382172d5b9e7eed59791b88381
4
- data.tar.gz: 2addb1393f4689b6dd0ab826c43a7dc9b0942e42fdc1ced57b153a910ef6e621
3
+ metadata.gz: 322c256e46337fbbb04715a5076984dda9e00ffa35e75bac7882fb8e95b17dea
4
+ data.tar.gz: 91390ce111e21fbf39f40e010bc059acab48d13a92b90f04d0a1a4b5738b87f8
5
5
  SHA512:
6
- metadata.gz: 26cad762bc6d533c7a1828550a371b7db6107fae7439d02176eb02b70bd8887c8a30f7b1c68b931b2088aee728c7e098bbdbf9eec1cefcac697d31ccaf8ebe09
7
- data.tar.gz: 7582b83233ce7ef198cc9a1d561b738396b5fe6d0b1701ba3984ec164735b3d8ce3fe3973481772217f7d580ec8b070d0493550859abfe0cbe03a5b8c9b6a738
6
+ metadata.gz: 78c783b0457ff798146af5c3040ccf86e3449dc33c53677986d58b7cd585745288bffcbf19f2427b8353c51507140b307209f33df2b4598d21d875c5203ab3a5
7
+ data.tar.gz: 257bbacb1c902bfa2202cd649b0d842f366963a528a00e115bc923a381d47bbe93e7c38c42cc21976f839d7a042873b107dc2fa8dd7ecc3703a61cecbe65e301
data/CHANGELOG.md CHANGED
@@ -6,6 +6,16 @@ Please notice that this change log contains changes for upcoming releases as wel
6
6
 
7
7
  ## Changes:
8
8
 
9
+ #### Change log v.0.7.58 (2024-04-28)
10
+
11
+ **Fix**: possible fix for compilation issues on Fedora. Credit to @garytaylor for opening issue #155.
12
+
13
+ **Fix**: possible fix for an OpenSSL certificate chain import issue that would cause certificate chains to be imported incorrectly. Credit to @dwolrdcojp for opening the facil.io repo PR #151.
14
+
15
+ #### Change log v.0.7.57 (2023-09-04)
16
+
17
+ **Fix**: Fixes possible name collision when loading gem (`.rb` vs. `.so` loading). Credit to @noraj (Alexandre ZANNI) for opening issue #148. Credit to @janbiedermann (Jan Biedermann) for discovering the root cause and offering a solution.
18
+
9
19
  #### Change log v.0.7.56 (2023-07-07)
10
20
 
11
21
  **Support**: Adds teapot support (HTTP code 418). Credit to Aleksandar N. Kostadinov (@akostadinov) for issue #144 and PR #145.
data/README.md CHANGED
@@ -10,28 +10,28 @@
10
10
 
11
11
  Iodine is a fast concurrent web application server for real-time Ruby applications, with native support for WebSockets and Pub/Sub services - but it's also so much more.
12
12
 
13
- Iodine is a Ruby wrapper for many of the [facil.io](https://facil.io) C framework, leveraging the speed of C for many common web application tasks. In addition, iodine abstracts away all network concerns, so you never need to worry about the transport layer, free to concentrate on your application logic.
13
+ Iodine is a Ruby wrapper for much of the [facil.io](https://facil.io) C framework, leveraging the speed of C for many common web application tasks. In addition, iodine abstracts away all network concerns, so you never need to worry about the transport layer, leaving you free to concentrate on your application logic.
14
14
 
15
15
  Iodine includes native support for:
16
16
 
17
17
  * HTTP, WebSockets and EventSource (SSE) Services (server);
18
18
  * WebSocket connections (server / client);
19
19
  * Pub/Sub (with optional Redis Pub/Sub scaling);
20
- * Fast(!) builtin Mustache template engine.
20
+ * Fast(!) builtin Mustache templating;
21
21
  * Static file service (with automatic `gzip` support for pre-compressed assets);
22
- * Optimized Logging to `stderr`.
22
+ * Optimized Logging to `stderr`;
23
23
  * Asynchronous event scheduling and timers;
24
24
  * HTTP/1.1 keep-alive and pipelining;
25
- * Heap Fragmentation Protection.
26
- * TLS 1.2 and above (Requires OpenSSL >= 1.1.0);
25
+ * Heap Fragmentation Protection;
26
+ * TLS 1.2 and above (Requiring OpenSSL >= 1.1.0);
27
27
  * TCP/IP server and client connectivity;
28
28
  * Unix Socket server and client connectivity;
29
- * Hot Restart (using the USR1 signal and without hot deployment);
29
+ * Hot Restarts (using the USR1 signal and without hot deployment);
30
30
  * Custom protocol authoring;
31
- * [Sequel](https://github.com/jeremyevans/sequel) and ActiveRecord forking protection.
31
+ * [Sequel](https://github.com/jeremyevans/sequel) and ActiveRecord forking protection;
32
32
  * and more!
33
33
 
34
- Since iodine wraps much of the [C facil.io framework](https://github.com/boazsegev/facil.io) to Ruby:
34
+ Since iodine wraps much of the [C facil.io framework](https://github.com/boazsegev/facil.io) for Ruby:
35
35
 
36
36
  * Iodine can handle **thousands of concurrent connections** (tested with more then 20K connections on Linux)!
37
37
 
@@ -39,13 +39,13 @@ Since iodine wraps much of the [C facil.io framework](https://github.com/boazseg
39
39
 
40
40
  Iodine is a C extension for Ruby, developed and optimized for Ruby MRI 2.3 and up... it should support the whole Ruby 2.x and 3.x MRI family, but CI tests start at Ruby 2.3.
41
41
 
42
- **Note**: iodine does **not** support streaming when using Rack. It's recommended to avoid blocking the server when using `body.each` since the `each` loop will block the iodine's thread until it's finished and iodine won't send any data before the loop is done.
42
+ **Note**: iodine does **not** support streaming when using Rack. It's recommended to avoid blocking the server when using `body.each` since the `each` loop will block iodine's thread until it's finished and iodine won't send any data before the loop is done.
43
43
 
44
44
  ## Iodine - a fast & powerful HTTP + WebSockets server with native Pub/Sub
45
45
 
46
46
  Iodine includes a light and fast HTTP and Websocket server written in C that was written according to the [Rack interface specifications](http://www.rubydoc.info/github/rack/rack/master/file/SPEC) and the [Websocket draft extension](./SPEC-Websocket-Draft.md).
47
47
 
48
- With `Iodine.listen service: :http` it's possible to run multiple HTTP applications (please remember not to set more than a single application on a single TCP/IP port).
48
+ With `Iodine.listen service: :http` it's possible to run multiple HTTP applications (but please remember not to set more than a single application on a single TCP/IP port).
49
49
 
50
50
  Iodine also supports native process cluster Pub/Sub and a native RedisEngine to easily scale iodine's Pub/Sub horizontally.
51
51
 
@@ -0,0 +1,3 @@
1
+ # Bates numbering with CombinePDF and Rack
2
+
3
+ This folder holds a demo application combining Iodine's `X-Sendfile` support with the [`combine_pdf` gem](https://github.com/boazsegev/combine_pdf) to create a "bates numbering" tool that is common to the one used by some court systems (such as Israeli courts).
@@ -0,0 +1,342 @@
1
+ require 'combine_pdf'
2
+ require 'rack'
3
+ require 'base64'
4
+ # require_relative 'pdf_controller'
5
+
6
+ module BatesAPP
7
+ # This is the HTTP response object according to the Rack specification.
8
+ STATIC_RESPONSE = [200, { 'Content-Type' => 'text/html', 'X-Sendfile' => File.expand_path('./public/index.html') }.freeze, []].freeze
9
+ WS_RESPONSE = [403, { 'Content-Type' => 'text/html' }, "Forbidden"]
10
+
11
+ # this is function will be called by the Rack server (iodine) for every request.
12
+ def self.call env
13
+ req = Rack::Request.new(env)
14
+ # check if this is an upgrade request (WebsSocket / SSE).
15
+ return WS_RESPONSE if(env['rack.upgrade?'.freeze])
16
+ # simply return the RESPONSE object, no matter what request was received.
17
+ return STATIC_RESPONSE if req.params.empty?
18
+ return bates(req.params)
19
+ end
20
+
21
+
22
+ def self.bates(params)
23
+ params['file'] = params['file'].values # convert to Array
24
+ # catch any exception and tell the user which PDF caused the exception.
25
+ begin
26
+ # set the file_name variable
27
+ # this will be used in case an exception is caught
28
+ # to state which file caused the exception.
29
+ file_name = ''
30
+
31
+ # container for the complete pdf
32
+ # the complete pdf container will hold all
33
+ # the merged pdf data.
34
+ completed_pdf = CombinePDF.new
35
+
36
+ # get the output file name
37
+ # this will be used to name the download for the client's browser
38
+ output_name = params['bates']['output'].to_s + '.pdf'
39
+ output_name = "unknown.pdf" if output_name == '.pdf'
40
+
41
+ # get some paramaters that will be used while combining pages
42
+ params['bates'] ||= {}
43
+ first_page_number = params['bates']['first_page_number'].to_i
44
+ first_index_number = params['bates']['first_index_number'].to_i
45
+
46
+ # we will add an option for the stamping to ignore the first pdf
47
+ # this is useful for the court cases that use bates numbering
48
+ # (the "cover PDF" will be the briefs of submissions that contain exhibits to be bates stamped)
49
+ ignore_first_file = params['bates']['first_pdf_is_cover']
50
+ first_page = nil
51
+
52
+ # we will pick some data up while running combining the different pdf files.
53
+ # this will be used for the table of contents later on.
54
+ pdfs_pages_count = []
55
+ pdf_dates = []
56
+ pdf_titles = []
57
+
58
+ # we will be creating title pages before each PDF file.
59
+ # the title pages will be sized using the mediabox variable
60
+ # wich will be set with the dimentions of each pdf file's first page.
61
+ mediabox = nil
62
+
63
+ # register UNICODE fonts if necessary
64
+ # in this example I will register the Hebrew font David from an existing PDF file.
65
+ unless CombinePDF::Fonts.get_font :my_new_david
66
+ # if your running Rails, consider Rails.root instead of Root
67
+ fonts = CombinePDF.new(File.expand_path('./david+bold.pdf').to_s).fonts(true)
68
+ # I know the first font is David regular, after using the
69
+ # ruby console and looking at the fonts array for the file.
70
+ CombinePDF.register_font_from_pdf_object :my_new_david, fonts[0]
71
+ # the second font of the array was the latin font for a newline and space... useless
72
+ # the third was the david bold. I will now add that font.
73
+ CombinePDF.register_font_from_pdf_object :my_new_david_bold, fonts[2]
74
+ end
75
+
76
+ # iterate through the different files sent from the client's browser's web form
77
+ params['file'].each do |v|
78
+ # set the file_name variable in case an exception will be raised
79
+ file_name = v['name']
80
+
81
+ # parse the pdf data
82
+ # we will use the CombinePDF.parse method which allows us
83
+ # to parse data without saving the PDF to the file system.
84
+ # our javascript encoded the file data using base64, which we will need to decode.
85
+ # (this is specific to my form which uses the HTML5 File API in this specific manner)
86
+ # begin
87
+ puts "parsing file: #{file_name}"
88
+ pdf_file = CombinePDF.parse(
89
+ Base64.urlsafe_decode64(
90
+ v['data'].slice('data:application/pdf;base64,'.length,
91
+ v['data'].length)
92
+ )
93
+ )
94
+ # rescue
95
+ # puts "Failed when parsing #{file_name} with data:\n#{v['data'].inspect}"
96
+ # raise
97
+ # end
98
+
99
+ # we will use the pages array a few times, so in order to avoid
100
+ # recomputing the array every time, we will save it to a local variable.
101
+ pdf_file_pages = pdf_file.pages
102
+
103
+ # we will add the page count to the page count array,
104
+ # used by the table of contents
105
+ pdfs_pages_count << pdf_file_pages.length
106
+
107
+ ######
108
+ # create and add title page to arrays.
109
+
110
+ if ignore_first_file && first_page.nil?
111
+ # if the first PDF file is a "cover page" PDF,
112
+ # we will not add a title, nor add the file.
113
+ # instead we will save the data in a variable to add it after we're done
114
+ pdf_dates << ''
115
+ pdf_titles << ''
116
+ first_page = pdf_file
117
+ else
118
+ # set title page mediabox size
119
+ # the mediabox data (page size) is contained in the page's
120
+ # :CropBox or :MediaBox keys (pages are Hash objects).
121
+ mediabox ||= pdf_file_pages[0][:CropBox] || pdf_file_pages[0][:MediaBox]
122
+
123
+ # create a title page unless we're only merging or there is no indexing
124
+ if params['bates']['should_index'] && params['bates']['numbering'] != 3
125
+
126
+ # create an empty page object
127
+ title_page = CombinePDF.create_page mediabox
128
+
129
+ # write the content to the title page.
130
+ # we will be using the I18n.t shortcut to write some of the data.
131
+ # the rest of the data, like the title, we got from the form.
132
+ title_page.textbox("#{params['bates']['title_type']} #{pdfs_pages_count.length + first_index_number - (ignore_first_file ? 2 : 1)}",
133
+ max_font_size: 34,
134
+ font: :my_new_david,
135
+ y: (mediabox[3] - mediabox[1]) / 2) unless params['bates']['title_type'].to_s.empty?
136
+ title_page.textbox v['title'].to_s, max_font_size: 36, font: :my_new_david_bold
137
+ title_page.textbox v['date'].to_s, max_font_size: 24, font: :my_new_david, height: (mediabox[3] - mediabox[1]) / 2
138
+
139
+ # we will add the page object to the completed pdf object.
140
+ # notice that page objects are created as "floating" pages,
141
+ # not attached to any specific PDF file/object.
142
+ completed_pdf << title_page
143
+
144
+ # we will add some data that will be used to create the
145
+ # table of contents at a later stage.
146
+ page_count = pdfs_pages_count.pop + 1
147
+ pdfs_pages_count << page_count
148
+ pdf_dates << v['date'].to_s
149
+ pdf_titles << v['title'].to_s
150
+ end
151
+
152
+ # now we are ready to add the pdf file data to the completed pdf object.
153
+ # there is no need to add each page, we can add the pdf as a whole.
154
+ # (it's actually faster, as the PDF page catalog isn't recomputed for each page)
155
+ completed_pdf << pdf_file
156
+ end
157
+ end
158
+
159
+ ##########
160
+ # create the index pdf...
161
+ # ...unless we're only merging or there is no indexing
162
+ if params['bates']['should_index'] && params['bates']['numbering'].to_i != 3
163
+
164
+ # set the fonts and formatting for the table of contents.
165
+ #
166
+ # also, add an empty array for the table data.
167
+ #
168
+ # the table data array will contain arrays of String objects, each one
169
+ # corresponding to a row in the table.
170
+ table_options = { font: :my_new_david,
171
+ header_font: :my_new_david_bold,
172
+ max_font_size: 12,
173
+ column_widths: (params['bates']['date_header'].to_s.empty? ? [3, 40, 4] : [3, 10, 30, 4]),
174
+ table_data: [] }
175
+
176
+ # set the table header array.
177
+ # this is an array of strings.
178
+ # to chose the localized strings
179
+ table_options[:headers] = [params['bates']['number_header'],
180
+ (params['bates']['date_header'].to_s.empty? ? nil : params['bates']['date_header']),
181
+ params['bates']['title_header'].to_s,
182
+ params['bates']['page_header'].to_s]
183
+ table_options[:headers].compact!
184
+
185
+ # by default, there are 25 rows per page for table pdf created by CombinePDF
186
+ # we can override this in the formatting (but we didn't).
187
+ #
188
+ # the 25 rows include 1 header row per page - so there are only 24 effective rows.
189
+ #
190
+ # we will calculate how many pages the table of contents pdf will have once completes,
191
+ # so we can add the count to the page numbers in the index.
192
+ index_page_length = pdfs_pages_count.length / 24
193
+ index_page_length += 1 if pdfs_pages_count.length % 24 > 0
194
+
195
+ # set the page number for the first entry in the table of contents.
196
+ page_number = first_page_number + index_page_length
197
+
198
+ # set the index count to 0, we will use it to change the index for each entry.
199
+ # we need a different variable in case the first PDF file is a "cover page".
200
+ index_count = 0
201
+
202
+ # iterate over the data we collected before and add it to the table data.
203
+ pdfs_pages_count.each_index do |i|
204
+ # add the data unless it is set to be ignored
205
+ unless ignore_first_file
206
+
207
+ # add an array of strings to the :table_data array,
208
+ # representing a row in our table.
209
+ # remember there might not be a date column.
210
+ if params['bates']['date_header'].to_s.empty?
211
+ table_options[:table_data] << [(first_index_number + index_count).to_s,
212
+ pdf_titles[i], page_number]
213
+ else
214
+ table_options[:table_data] << [(first_index_number + index_count).to_s,
215
+ pdf_dates[i],
216
+ pdf_titles[i], page_number]
217
+ end
218
+
219
+ # if the data was added to the index table, bump the index count
220
+ index_count += 1
221
+
222
+ end
223
+
224
+ # make sure future data will not be ignored
225
+ ignore_first_file = false
226
+
227
+ # add the page count to the page number, so that the next
228
+ # index's page number is up to date.
229
+ page_number += pdfs_pages_count[i]
230
+ end
231
+
232
+ # if out current locale is hebrew, which is a right to left language,
233
+ # set the direction for the table to Right-To-left (:rtl).
234
+ #
235
+ # notice that RTL text should be automatically recognized, but that
236
+ # feature isn't available (and shouldn't be available) for tables.
237
+ table_options[:direction] = :rtl if params['bates']['dir'] == 'rtl'
238
+
239
+ # if there is table data, we will create an index pdf.
240
+ unless table_options[:table_data].empty?
241
+
242
+ # create the index PDF from the table data and options we have.
243
+ index_pdf = CombinePDF.create_table table_options
244
+
245
+ # We will now add the words "Table of Contents" (or the I18n equivilant)
246
+ # to the first page of our new index_pdf PDF object.
247
+ #
248
+ # the table PDF object was created by CombinePDF using writable PDF pages,
249
+ # so we have properties like .mediabox and methods like .textbox
250
+ # at our disposal.
251
+
252
+ # get the first page of the index_pdf object, we will use this reference a lot.
253
+ title_page = index_pdf.pages[0]
254
+
255
+ # write the textbox, using the mediabox page data [x,y,width,height] to place
256
+ # the text we want to write.
257
+ #
258
+ # we will use the I18n.t shortcut to choose the text to write down.
259
+ title_page.textbox params['bates']['index_title'],
260
+ y: ((title_page.mediabox[3] - title_page.mediabox[1]) * 0.91),
261
+ height: ((title_page.mediabox[3] - title_page.mediabox[1]) * 0.03),
262
+ font: :my_new_david,
263
+ max_font_size: 36,
264
+ text_valign: :bottom
265
+
266
+ # now we will add the index_pdf to the BEGINING of the completed pdf.
267
+ # for this we will use the >> operator instead of the << operator.
268
+ completed_pdf >> index_pdf
269
+
270
+ end
271
+
272
+ end
273
+
274
+ # add first file if it was skipped
275
+ completed_pdf >> first_page unless first_page.nil?
276
+
277
+ #####
278
+ # number pages
279
+ # unless no numbering
280
+ if params['bates']['numbering'].to_i != 3
281
+ # list the numbering options
282
+ numbering_options = [[:top, :bottom], [:top_left, :bottom_left], [:top_right, :bottom_right]]
283
+
284
+ # set the first visible page number to the page where numbering starts
285
+ # this assumes that the bates numbering include the numbering of the "cover page",
286
+ # yet at the same time the numbering isn't visible on the "cover page"
287
+ first_page_number += pdfs_pages_count[0] if params['bates']['first_pdf_is_cover'] && params['bates']['skip_cover']
288
+
289
+ # call the page numbering method and
290
+ # add the special properties we want for the textbox
291
+ completed_pdf.number_pages(start_at: first_page_number,
292
+ page_range: params['bates']['skip_cover'] ? (pdfs_pages_count[0].to_i..-1) : nil,
293
+ font_name: :my_new_david,
294
+ font_size: 14,
295
+ font_color: [0, 0, 0.4],
296
+ box_color: [0.8, 0.8, 0.8],
297
+ border_width: 1,
298
+ border_color: [0.3, 0.3, 0.3],
299
+ box_radius: 8,
300
+ number_location: numbering_options[params['bates']['numbering'].to_i],
301
+ opacity: 0.75)
302
+ end
303
+
304
+ # send the completed PDF to the client.
305
+ # if the completed PDF is empty, raise an error.
306
+ if completed_pdf.pages.empty?
307
+ # inform the client there was an unknown error.
308
+ response = STATIC_RESPONSE.dup
309
+ response[1] = response[1].dup
310
+ response[1]['set-cookie'] = "Unknown error - 0 pages."
311
+ return response
312
+ end
313
+
314
+ # make sure the PDF version is high enough for the opacity we used in the page numbering.
315
+ completed_pdf.version = [completed_pdf.version, 1.6].max
316
+
317
+ # we will format the PDF to a pdf file WITHOUT saving it to the file system,
318
+ # using the .to_pdf method (instead of the .save method).
319
+ # we will send the raw PDF data stream.
320
+ return [200, {'content-type' => "application/pdf",
321
+ 'content-disposition' => "attachment; filename=\"#{output_name}\""}, completed_pdf.to_pdf]
322
+
323
+ rescue Exception => e
324
+ puts e.message, e.backtrace
325
+ puts "The file causing the exception is: #{file_name}"
326
+ # if an exception was raised, tell the user which PDF caused the exception
327
+ response = STATIC_RESPONSE.dup
328
+ response[1] = response[1].dup
329
+ response[1]['set-cookie'] = "notice=Unsupported or error reading file: #{file_name}"
330
+ return response
331
+ end
332
+ end
333
+ end
334
+
335
+ Iodine.workers = 1
336
+ Iodine.threads = 5
337
+ Iodine::DEFAULT_SETTINGS[:public] ||= './public'
338
+
339
+ p Iodine::DEFAULT_SETTINGS
340
+ Iodine::DEFAULT_SETTINGS[:max_body] = 70 if Iodine::DEFAULT_SETTINGS[:max_body].to_i < 5
341
+
342
+ run BatesAPP
Binary file
Binary file