SlideShare a Scribd company logo
What is Rack
Hijacking API
2016-12-03 at rubyconf.tw
1
Who am I?
• Kiyoshi Nomo
• @kysnm
• Web Application Engineer
• Goodpatch, Inc.
http://goodpatch.com/
https://prottapp.com/
2
Agenda
• The Basics
• About the SPEC
• About the implementation
• Take a quick look at ActionCable
• Conclusion
3
The Basics
4
Who made this API?
5
6
Why it was made?
• Rack didn't have an API that allows
for IO-like streaming.
• for WebSocket
• for HTTP2
https://github.com/rack/rack/pull/
481#issue-9702395
7
Similar implementation
• Golang's Hijacker interface.
• Probably, This API would made
based on this interface.
https://github.com/rack/rack/pull/
481#issue-9702395
8
Support Servers
• puma
• passenger
• thin
• webrick (only partial hijack is
supported.)
• etc…
9
About the SPEC
10
Two mode of Hijaking
• Full hijacking
• Partial hijacking
http://www.rubydoc.info/github/
rack/rack/master/file/
SPEC#Hijacking
11
The timing of Full
hijacking
• Request (before status)
12
The conditions of Full
hijacking
env['rack.hijack?'] == true
env['rack.hijack'].respond_to?(:call) == true
env['rack.hijack'].call must returns the io
env['rack.hijack'].call is assigned the io to
env['rack.hijack_io']
REQUIRED_METHOD =
[:read, :write, :read_nonblock, :write_nonblock, :fl
ush, :close, :close_read, :close_write, :closed?]
REQUIRED_METHOD.all? { |m|
env['rack.hijack_io'].respond_to?(m) } == true
13
Your responsibility of
Full hijacking
• Outputting any HTTP headers, if
applicable.
• Closing the IO object when you no
longer need it.
14
class HijackWrapper
include Assertion
extend Forwardable
REQUIRED_METHODS = [
:read, :write, :read_nonblock, :write_nonblock, :flush, :clos
e,
:close_read, :close_write, :closed?
]
def_delegators :@io, *REQUIRED_METHODS
def initialize(io)
@io = io
REQUIRED_METHODS.each do |meth|
assert("rack.hijack_io must respond to #{meth}")
{ io.respond_to? meth }
end
end
end
https://github.com/rack/rack/blob/
fd1fbab1ec8c7fc49ac805aac47b1f12d4cc5a99/lib/rack/lint.rb#L494-
L511
15
def check_hijack(env)
if env[RACK_IS_HIJACK]
original_hijack = env[RACK_HIJACK]
assert("rack.hijack must respond to call")
{ original_hijack.respond_to?(:call) }
env[RACK_HIJACK] = proc do
io = original_hijack.call
HijackWrapper.new(io)
env[RACK_HIJACK_IO] =
HijackWrapper.new(env[RACK_HIJACK_IO])
io
end
else
assert("rack.hijack? is false, but rack.hijack is
present") { env[RACK_HIJACK].nil? }
assert("rack.hijack? is false, but rack.hijack_io is
present") { env[RACK_HIJACK_IO].nil? }
end
end
https://github.com/rack/rack/blob/
fd1fbab1ec8c7fc49ac805aac47b1f12d4cc5a99/lib/rack/
lint.rb#L513-L562
16
The timing of Partial
hijacking
• Response (after headers)
17
The conditions of
Partial hijacking
• an application may set the special
header rack.hijack to an object
that responds to #call accepting
an argument that conforms to the
rack.hijack_io protocol.
18
Your responsibility of
Partial hijacking
• closing the socket when it’s no
longer needed.
19
def check_hijack_response(headers, env)
headers = Rack::Utils::HeaderHash.new(headers)
if env[RACK_IS_HIJACK] && headers[RACK_HIJACK]
assert('rack.hijack header must respond to #call') {
headers[RACK_HIJACK].respond_to? :call
}
original_hijack = headers[RACK_HIJACK]
headers[RACK_HIJACK] = proc do |io|
original_hijack.call HijackWrapper.new(io)
end
else
assert('rack.hijack header must not be present if server
does not support hijacking') {
headers[RACK_HIJACK].nil?
}
end
end
https://github.com/rack/rack/blob/
fd1fbab1ec8c7fc49ac805aac47b1f12d4cc5a99/lib/rack/
lint.rb#L564-L614
20
About the
implementation
21
Introduce two servers
• rack (webrick)
• puma
22
Webrick (rack)
23
Webrick is
• supported only partial hijack.
24
How to configure?
• See the test/spec_webrick.rb
25
it "support Rack partial hijack" do
io_lambda = lambda{ |io|
5.times do
io.write "Davidrn"
end
io.close
}
@server.mount "/partial", Rack::Handler::WEBrick,
Rack::Lint.new(lambda{ |req|
[
200,
[ [ "rack.hijack", io_lambda ] ],
[""]
]
})
Net::HTTP.start(@host, @port){ |http|
res = http.get("/partial")
res.body.must_equal "DavidrnDavidrnDavidrnDavidrnDavidrn"
}
end
https://github.com/rack/rack/blob/
cabe6b33ca4601aa6acb56317ac1c819cf6dc4bb/test/spec_webrick.rb#L162-L183
26
run lambda { |env|
io_lambda = lambda { |io|
i = 1
5.times do
io.write "Davidrn"
end
io.close
}
[
200,
[ [ 'rack.hijack', io_lambda ] ],
['']
]
}
27
Rack::Handler::Webrick::run
def self.run(app, options={})
environment = ENV['RACK_ENV'] || 'development'
default_host = environment == 'development' ? 'localhost' :
nil
options[:BindAddress] = options.delete(:Host) || default_host
options[:Port] ||= 8080
@server = ::WEBrick::HTTPServer.new(options)
@server.mount "/", Rack::Handler::WEBrick, app
yield @server if block_given?
@server.start
end
https://github.com/rack/rack/blob/
cabe6b33ca4601aa6acb56317ac1c819cf6dc4bb/lib/rack/handler/
webrick.rb#L25-L35
app
[1] pry(#<Rack::Handler::WEBrick>)> app
=> #<Rack::ContentLength:0x007fa0fa17f2a8
@app=
#<Rack::Chunked:0x007fa0fa17f2f8
@app=
#<Rack::CommonLogger:0x007fa0fa17f348
@app=
#<Rack::ShowExceptions:0x007fa0fb208458
@app=
#<Rack::Lint:0x007fa0fb2084a8
@app=
#<Rack::TempfileReaper:0x007fa0fb208520
@app=#<Proc:0x007fa0fb368c08@/tmp/rack_hijack_test/
webrick/config.ru:1 (lambda)>>,
@content_length=nil>>,
@logger=#<IO:<STDERR>>>>>
Webrick::HTTPServer#servic
e
si = servlet.get_instance(self,
*options)
@logger.debug(format("%s is
invoked.", si.class.name))
si.service(req, res)
https://github.com/ruby/ruby/blob/
v2_3_3/lib/webrick/
httpserver.rb#L138-L140
Webrick::HTTPServlet::Abstr
actServlet::get_instance
def self.get_instance(server,
*options)
self.new(server, *options)
end
https://github.com/ruby/ruby/blob/
v2_3_3/lib/webrick/httpservlet/
abstract.rb#L85-L87
Rack::Handler::Webrick#initi
alize
def initialize(server, app)
super server
@app = app
end
https://github.com/rack/rack/blob/
cabe6b33ca4601aa6acb56317ac1c819cf6
dc4bb/lib/rack/handler/
webrick.rb#L52-L55
Rack::Handler::Webrick#serv
ice (Take out the io_lambda)
status, headers, body = @app.call(env)
begin
res.status = status.to_i
io_lambda = nil
headers.each { |k, vs|
if k == RACK_HIJACK
io_lambda = vs
elsif k.downcase == "set-cookie"
res.cookies.concat vs.split("n")
else
# Since WEBrick won't accept repeated headers,
# merge the values per RFC 1945 section 4.2.
res[k] = vs.split("n").join(", ")
end
}
https://github.com/rack/rack/blob/cabe6b33ca4601aa6acb56317ac1c819cf6dc4bb/
lib/rack/handler/webrick.rb#L86-L100
Rack::Handler::Webrick#serv
ice (Calls the io_lambda)
if io_lambda
rd, wr = IO.pipe
res.body = rd
res.chunked = true
io_lambda.call wr
elsif body.respond_to?(:to_path)
res.body = ::File.open(body.to_path, 'rb')
else
body.each { |part|
res.body << part
}
end
ensure
body.close if body.respond_to? :close
end
https://github.com/rack/rack/blob/cabe6b33ca4601aa6acb56317ac1c819cf6dc4bb/
lib/rack/handler/webrick.rb#L86-L100
response
<= Recv data, 35 bytes (0x23)
0000: David
0007: David
000e: David
0015: David
001c: David
== Info: transfer closed with outstanding read data
remaining
== Info: Curl_http_done: called premature == 1
== Info: Closing connection 0
https://gist.github.com/kysnm/
ca5237d4ac96764b9cfe6ac1547710cf
puma
36
puma is
• threaded, cluster enabled server.
• supported two mode of hijacking.
37
Full hijacking example
run lambda { |env|
io = env['rack.hijack'].call
io.puts "HTTP/1.1 200rnr
nBLAH"
[-1, {}, []]
}
https://github.com/puma/puma/blob/
3.6.1/test/hijack.ru
38
Before
Puma::Runner#start_server
=> #0 start_server
<Puma::Runner#start_server()>
#1 [method] start_server
<Puma::Runner#start_server()>
#2 [method] run
<Puma::Single#run()>
#3 [method] run
<Puma::Launcher#run()>
#4 [method] run <Puma::CLI#run()>
39
Puma::Runner#start_serv
er
def start_server
min_t = @options[:min_threads]
max_t = @options[:max_threads]
server = Puma::Server.new app, @launcher.events, @options
server.min_threads = min_t
server.max_threads = max_t
server.inherit_binder @launcher.binder
if @options[:mode] == :tcp
server.tcp_mode!
end
unless development?
server.leak_stack_on_error = false
end
server
end
https://github.com/puma/puma/blob/3.6.1/lib/puma/runner.rb#L140-L160
40
app
[1] pry(#<Puma::Server>)> app
=> #<Puma::Configuration::ConfigMiddleware:0x007ffaf2badc50
@app=#<Proc:0x007ffaf2badfc0@puma/hijack.ru:1 (lambda)>,
@config=
#<Puma::Configuration:0x007ffaf2c75110
@options=
#<Puma::LeveledOptions:0x007ffaf2c74f08
@cur={},
@defaults=
{:min_threads=>0,
:max_threads=>16,
:log_requests=>false,
:debug=>false,
:binds=>["tcp://0.0.0.0:9292"],
:workers=>0,
… snip …
41
Puma::Single#run
begin
server.run.join
rescue Interrupt
# Swallow it
end
https://github.com/puma/puma/blob/
3.6.1/lib/puma/single.rb#L103-L107
42
Puma::Server#handle_ser
vers
if io = sock.accept_nonblock
client = Client.new io, @binder.env(sock)
if remote_addr_value
client.peerip = remote_addr_value
elsif remote_addr_header
client.remote_addr_header = remote_addr_header
end
pool << client
pool.wait_until_not_full unless queue_requests
end
https://github.com/puma/puma/blob/3.6.1/lib/puma/
server.rb#L333-L343
43
Before
Puma::ThreadPool#spawn_thre
ad
=> #0 spawn_thread
<Puma::ThreadPool#spawn_thread()>
#1 [method] spawn_thread
<Puma::ThreadPool#spawn_thread()>
#2 [block] block in <<
<Puma::ThreadPool#<<(work)>
#3 [method] << <Puma::ThreadPool#<<(work)>
#4 [block] block in handle_servers
<Puma::Server#handle_servers()>
#5 [method] handle_servers
<Puma::Server#handle_servers()>
#6 [block] block in run
<Puma::Server#run(background=?)>
44
Puma::Server#run
(block)
process_client client, buffer
https://github.com/puma/puma/blob/
3.6.1/lib/puma/server.rb#L275
45
Puma::Server#process_cl
ient
while true
case handle_request(client, buffer)
when false
return
when :async
close_socket = false
return
when true
return unless @queue_requests
buffer.reset
https://github.com/puma/puma/blob/3.6.1/lib/
puma/server.rb#L275
46
Puma::Server#handle_req
uest (arguments)
def handle_request(req, lines)
env = req.env
client = req.io
normalize_env env, req
env[PUMA_SOCKET] = client
https://github.com/puma/puma/blob/
3.6.1/lib/puma/server.rb#L549-L555
47
Puma::Server#handle_req
uest (HIJACK_P, HIJACK)
env[HIJACK_P] = true
env[HIJACK] = req
https://github.com/puma/puma/blob/
3.6.1/lib/puma/server.rb#L561-L562
48
Puma::Client#call
# For the hijack protocol (allows us
to just put the Client object
# into the env)
def call
@hijacked = true
env[HIJACK_IO] ||= @io
end
https://github.com/puma/puma/blob/
3.6.1/lib/puma/client.rb#L69-L74
49
Puma::Const
HIJACK_P = "rack.hijack?".freeze
HIJACK = "rack.hijack".freeze
HIJACK_IO =
"rack.hijack_io".freeze
https://github.com/puma/puma/blob/
3.6.1/lib/puma/const.rb#L249-L251
50
Puma::Server#handle_req
uest (@app.call)
begin
begin
status, headers, res_body =
@app.call(env)
return :async if req.hijacked
https://github.com/puma/puma/blob/
3.6.1/lib/puma/server.rb#L576-L580
51
Partial hijacking
example
run lambda { |env|
body = lambda { |io| io.puts "BLAH
n"; io.close }
[200, { 'rack.hijack' => body },
[]]
}
https://github.com/puma/puma/blob/
3.6.1/test/hijack2.ru
52
Puma::Server#handle_req
uest (@app.call)
begin
begin
status, headers, res_body =
@app.call(env)
return :async if req.hijacked
https://github.com/puma/puma/blob/
3.6.1/lib/puma/server.rb#L576-L580
53
Puma::Server#handle_req
uest (response_hijack)
response_hijack = nil
headers.each do |k, vs|
case k.downcase
when CONTENT_LENGTH2
content_length = vs
next
when TRANSFER_ENCODING
allow_chunked = false
content_length = nil
when HIJACK
response_hijack = vs
next
end
https://github.com/puma/puma/blob/3.6.1/lib/puma/server.rb#L653-
L666
54
Puma::Server#handle_reque
st (response_hijack.call)
if response_hijack
response_hijack.call client
return :async
end
https://github.com/puma/puma/blob/
3.6.1/lib/puma/server.rb#L705-L708
55
Take a quick look at
ActionCable
56
In
ActionCable::Connection::St
ream
57
ActionCable::Connection::
Stream#hijack_rack_socket
def hijack_rack_socket
return unless @socket_object.env['rack.hijack']
@socket_object.env['rack.hijack'].call
@rack_hijack_io =
@socket_object.env['rack.hijack_io']
@event_loop.attach(@rack_hijack_io, self)
end
https://github.com/rails/rails/blob/v5.0.0.1/
actioncable/lib/action_cable/connection/
stream.rb#L40-L47
58
ActionCable::Connection::
Stream#clean_rack_hijack
private
def clean_rack_hijack
return unless @rack_hijack_io
@event_loop.detach(@rack_hijack_io,
self)
@rack_hijack_io = nil
end
https://github.com/rails/rails/blob/
v5.0.0.1/actioncable/lib/action_cable/
connection/stream.rb#L40-L47
59
Faye::RackStream#hijack
_rack_socket 1
def hijack_rack_socket
return unless
@socket_object.env['rack.hijack']
@socket_object.env['rack.hijack'].call
@rack_hijack_io =
@socket_object.env['rack.hijack_io']
queue = Queue.new
https://github.com/faye/faye-websocket-
ruby/blob/0.10.5/lib/faye/
rack_stream.rb#L30-L36
60
Faye::RackStream#hijack
_rack_socket 2
EventMachine.schedule do
begin
EventMachine.attach(@rack_hijack_io,
Reader) do |reader|
reader.stream = self
if @rack_hijack_io
@rack_hijack_io_reader = reader
else
reader.close_connection_after_writing
end
https://github.com/faye/faye-websocket-ruby/
blob/0.10.5/lib/faye/rack_stream.rb#L37-L46
61
Faye::RackStream#hijack
_rack_socket 3
ensure
queue.push(nil)
end
end
queue.pop if
EventMachine.reactor_running?
end
https://github.com/faye/faye-websocket-
ruby/blob/0.10.5/lib/faye/
rack_stream.rb#L47-L53
62
Faye::RackStream#clean_
rack_hijack
def clean_rack_hijack
return unless @rack_hijack_io
@rack_hijack_io_reader.close_connection_afte
r_writing
@rack_hijack_io = @rack_hijack_io_reader =
nil
end
https://github.com/faye/faye-websocket-ruby/
blob/0.10.5/lib/faye/rack_stream.rb#L55-L59
63
Conclusion
64
Limitations
•I have not tried to spec out a full IO
API, and I'm not sure that we should.
•I have not tried to respec all of the
HTTP / anti-HTTP semantics.
•There is no spec for buffering or the
like.
•The intent is that this is an API to
"get out the way”.
https://github.com/rack/rack/pull/481
65
What?
this is a straw man that addresses this within
the confines of the rack 1.x spec. It's not an
attempt to build out what I hope a 2.0 spec
should be, but I am hoping that something like
this will be enough to aid Rails 4s ventures,
enable websockets, and a few other strategies.
With HTTP2 around the corner, we'll likely
want to revisit the IO API for 2.0, but we'll
see how this plays out. Maybe IO wrapped
around channels will be ok.
https://github.com/rack/rack/pull/481
66
Thank you.
67
Reference
• http://www.rubydoc.info/github/rack/rack/
master/file/SPEC#Hijacking
• http://old.blog.phusion.nl/2013/01/23/the-
new-rack-socket-hijacking-api/
• https://github.com/rack/rack/pull/481

More Related Content

PDF
流体解析入門者向け超初級講習会
PPTX
CloudFront経由でのCORS利用
PDF
containerdの概要と最近の機能
PDF
XcodeのTargetについてのTIPS
PDF
64ビット対応Dllインジェクション
PDF
Office365のIdentity管理
PDF
Btrfsの基礎 part1 機能編
PDF
Xcode グループとフォルダー参照 #yhios
流体解析入門者向け超初級講習会
CloudFront経由でのCORS利用
containerdの概要と最近の機能
XcodeのTargetについてのTIPS
64ビット対応Dllインジェクション
Office365のIdentity管理
Btrfsの基礎 part1 機能編
Xcode グループとフォルダー参照 #yhios

What's hot (20)

PPTX
【初心者理解用】分散型金融(DeFI)と保険ビジネス
PDF
Docker道場オンライン#1 Docker基礎概念と用語の理解
PDF
1075: .NETからCUDAを使うひとつの方法
PPTX
Fluentd1.2 & Fluent Bit
PPTX
Η ιστορία του βιβλίου και της τυπογραφίας
PPTX
dachnug51 - BSI IT Grundschutz leicht gemacht.pptx
PDF
Hub内でラクラク決済!ーVRコマース拡張ー
PPTX
Stack pivot
PDF
Guide hse 2009
PPTX
مخاطر الغاز الطبيعي
PPTX
Docker friendly PHP / Laravel
PDF
#idcon vol.29 - #fidcon WebAuthn, Next Stage
PDF
AWS Black Belt Techシリーズ Amazon Kinesis
PPTX
node-gypを使ったネイティブモジュールの作成
PDF
プログラムを高速化する話
PDF
Incendie hse
PDF
いまさら聞けないパスワードの取り扱い方
PDF
Stargz Snapshotter: イメージのpullを省略しcontainerdでコンテナを高速に起動する
PPTX
Serverless Meetup Tokyo #15 Amazon EventBridge スキーマレジストリ でイベントの扱いを簡単に!
PPTX
ここがつらいよAws batch
【初心者理解用】分散型金融(DeFI)と保険ビジネス
Docker道場オンライン#1 Docker基礎概念と用語の理解
1075: .NETからCUDAを使うひとつの方法
Fluentd1.2 & Fluent Bit
Η ιστορία του βιβλίου και της τυπογραφίας
dachnug51 - BSI IT Grundschutz leicht gemacht.pptx
Hub内でラクラク決済!ーVRコマース拡張ー
Stack pivot
Guide hse 2009
مخاطر الغاز الطبيعي
Docker friendly PHP / Laravel
#idcon vol.29 - #fidcon WebAuthn, Next Stage
AWS Black Belt Techシリーズ Amazon Kinesis
node-gypを使ったネイティブモジュールの作成
プログラムを高速化する話
Incendie hse
いまさら聞けないパスワードの取り扱い方
Stargz Snapshotter: イメージのpullを省略しcontainerdでコンテナを高速に起動する
Serverless Meetup Tokyo #15 Amazon EventBridge スキーマレジストリ でイベントの扱いを簡単に!
ここがつらいよAws batch
Ad

Similar to What is Rack Hijacking API (19)

PDF
Introducing Rack
PDF
Intro to Rack
PDF
PDF
Rhebok, High Performance Rack Handler / Rubykaigi 2015
PDF
Rack - O hério oculto de aplicações web Rub.pdf
PDF
Rack
PDF
Ruby MVC from scratch with Rack
PPTX
2022 APIsecure_Making webhook APIs secure for enterprise
PDF
Building web framework with Rack
KEY
Using and scaling Rack and Rack-based middleware
KEY
Rack
PPTX
Rails Request & Middlewares
PDF
Ruxmon feb 2013 what happened to rails
PDF
Web Clients for Ruby and What they should be in the future
PPTX
What the rack
KEY
Rack Middleware
PDF
Serverless Security: A How-to Guide @ SnowFROC 2019
Introducing Rack
Intro to Rack
Rhebok, High Performance Rack Handler / Rubykaigi 2015
Rack - O hério oculto de aplicações web Rub.pdf
Rack
Ruby MVC from scratch with Rack
2022 APIsecure_Making webhook APIs secure for enterprise
Building web framework with Rack
Using and scaling Rack and Rack-based middleware
Rack
Rails Request & Middlewares
Ruxmon feb 2013 what happened to rails
Web Clients for Ruby and What they should be in the future
What the rack
Rack Middleware
Serverless Security: A How-to Guide @ SnowFROC 2019
Ad

Recently uploaded (20)

DOCX
ASol_English-Language-Literature-Set-1-27-02-2023-converted.docx
PDF
PREDICTION OF DIABETES FROM ELECTRONIC HEALTH RECORDS
PPTX
FINAL REVIEW FOR COPD DIANOSIS FOR PULMONARY DISEASE.pptx
PPTX
Current and future trends in Computer Vision.pptx
PPTX
Foundation to blockchain - A guide to Blockchain Tech
PPTX
Artificial Intelligence
PPTX
bas. eng. economics group 4 presentation 1.pptx
PDF
The CXO Playbook 2025 – Future-Ready Strategies for C-Suite Leaders Cerebrai...
PDF
BMEC211 - INTRODUCTION TO MECHATRONICS-1.pdf
PPT
Mechanical Engineering MATERIALS Selection
PDF
Embodied AI: Ushering in the Next Era of Intelligent Systems
PPTX
CARTOGRAPHY AND GEOINFORMATION VISUALIZATION chapter1 NPTE (2).pptx
PPTX
CYBER-CRIMES AND SECURITY A guide to understanding
PPTX
CH1 Production IntroductoryConcepts.pptx
PDF
III.4.1.2_The_Space_Environment.p pdffdf
PPTX
Infosys Presentation by1.Riyan Bagwan 2.Samadhan Naiknavare 3.Gaurav Shinde 4...
PPTX
UNIT-1 - COAL BASED THERMAL POWER PLANTS
PPTX
OOP with Java - Java Introduction (Basics)
PPTX
additive manufacturing of ss316l using mig welding
PPTX
MET 305 2019 SCHEME MODULE 2 COMPLETE.pptx
ASol_English-Language-Literature-Set-1-27-02-2023-converted.docx
PREDICTION OF DIABETES FROM ELECTRONIC HEALTH RECORDS
FINAL REVIEW FOR COPD DIANOSIS FOR PULMONARY DISEASE.pptx
Current and future trends in Computer Vision.pptx
Foundation to blockchain - A guide to Blockchain Tech
Artificial Intelligence
bas. eng. economics group 4 presentation 1.pptx
The CXO Playbook 2025 – Future-Ready Strategies for C-Suite Leaders Cerebrai...
BMEC211 - INTRODUCTION TO MECHATRONICS-1.pdf
Mechanical Engineering MATERIALS Selection
Embodied AI: Ushering in the Next Era of Intelligent Systems
CARTOGRAPHY AND GEOINFORMATION VISUALIZATION chapter1 NPTE (2).pptx
CYBER-CRIMES AND SECURITY A guide to understanding
CH1 Production IntroductoryConcepts.pptx
III.4.1.2_The_Space_Environment.p pdffdf
Infosys Presentation by1.Riyan Bagwan 2.Samadhan Naiknavare 3.Gaurav Shinde 4...
UNIT-1 - COAL BASED THERMAL POWER PLANTS
OOP with Java - Java Introduction (Basics)
additive manufacturing of ss316l using mig welding
MET 305 2019 SCHEME MODULE 2 COMPLETE.pptx

What is Rack Hijacking API