swiftiply 0.5.1 → 0.6.0
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.
- data/CONTRIBUTORS +2 -0
- data/README +1 -1
- data/bin/echo_client +26 -0
- data/bin/swiftiply +21 -8
- data/ext/fastfilereader/extconf.rb +161 -0
- data/ext/fastfilereader/mapper.cpp +200 -0
- data/ext/fastfilereader/mapper.h +59 -0
- data/ext/fastfilereader/rubymain.cpp +127 -0
- data/external/test_support.rb +13 -0
- data/setup.rb +7 -2
- data/src/fastfilereader.rb +109 -0
- data/src/swiftcore/Swiftiply.rb +432 -103
- data/src/swiftcore/Swiftiply/support_pagecache.rb +56 -0
- data/src/swiftcore/Swiftiply/swiftiply_client.rb +57 -0
- data/src/swiftcore/evented_mongrel.rb +32 -4
- data/src/swiftcore/swiftiplied_mongrel.rb +50 -38
- data/src/swiftcore/types.rb +1583 -0
- data/swiftiply.gemspec +4 -4
- data/test/TC_ProxyBag.rb +185 -0
- data/test/TC_Swiftiply.rb +458 -0
- data/test/TC_Swiftiply/mongrel/evented_hello.rb +25 -0
- data/test/TC_Swiftiply/mongrel/swiftiplied_hello.rb +25 -0
- data/test/TC_Swiftiply/mongrel/threaded_hello.rb +25 -0
- data/test/TC_Swiftiply/slow_echo_client +26 -0
- metadata +34 -121
- data/src/ramaze/adapter/evented_mongrel.rb +0 -2
- data/src/ramaze/adapter/swiftiplied_mongrel.rb +0 -2
- data/test/rails/README +0 -182
- data/test/rails/Rakefile +0 -10
- data/test/rails/app/controllers/application.rb +0 -6
- data/test/rails/app/controllers/tests_controller.rb +0 -15
- data/test/rails/app/helpers/application_helper.rb +0 -3
- data/test/rails/config/boot.rb +0 -45
- data/test/rails/config/database.yml +0 -36
- data/test/rails/config/environment.rb +0 -60
- data/test/rails/config/environments/development.rb +0 -21
- data/test/rails/config/environments/production.rb +0 -18
- data/test/rails/config/environments/production_no_caching.rb +0 -18
- data/test/rails/config/environments/test.rb +0 -19
- data/test/rails/config/routes.rb +0 -23
- data/test/rails/doc/README_FOR_APP +0 -2
- data/test/rails/observe_ram.rb +0 -10
- data/test/rails/public/404.html +0 -30
- data/test/rails/public/500.html +0 -30
- data/test/rails/public/dispatch.cgi +0 -10
- data/test/rails/public/dispatch.fcgi +0 -24
- data/test/rails/public/dispatch.rb +0 -10
- data/test/rails/public/favicon.ico +0 -0
- data/test/rails/public/images/rails.png +0 -0
- data/test/rails/public/index.html +0 -277
- data/test/rails/public/javascripts/application.js +0 -2
- data/test/rails/public/javascripts/controls.js +0 -833
- data/test/rails/public/javascripts/dragdrop.js +0 -942
- data/test/rails/public/javascripts/effects.js +0 -1088
- data/test/rails/public/javascripts/prototype.js +0 -2515
- data/test/rails/public/robots.txt +0 -1
- data/test/rails/script/about +0 -3
- data/test/rails/script/breakpointer +0 -3
- data/test/rails/script/console +0 -3
- data/test/rails/script/destroy +0 -3
- data/test/rails/script/generate +0 -3
- data/test/rails/script/performance/benchmarker +0 -3
- data/test/rails/script/performance/profiler +0 -3
- data/test/rails/script/plugin +0 -3
- data/test/rails/script/process/inspector +0 -3
- data/test/rails/script/process/reaper +0 -3
- data/test/rails/script/process/spawner +0 -3
- data/test/rails/script/runner +0 -3
- data/test/rails/script/server +0 -3
- data/test/rails/test/test_helper.rb +0 -28
- data/test/ramaze/conf/benchmark.yaml +0 -35
- data/test/ramaze/conf/debug.yaml +0 -34
- data/test/ramaze/conf/live.yaml +0 -33
- data/test/ramaze/conf/silent.yaml +0 -31
- data/test/ramaze/conf/stage.yaml +0 -33
- data/test/ramaze/main.rb +0 -18
- data/test/ramaze/public/404.jpg +0 -0
- data/test/ramaze/public/css/coderay.css +0 -105
- data/test/ramaze/public/css/ramaze_error.css +0 -42
- data/test/ramaze/public/error.zmr +0 -77
- data/test/ramaze/public/favicon.ico +0 -0
- data/test/ramaze/public/js/jquery.js +0 -1923
- data/test/ramaze/public/ramaze.png +0 -0
- data/test/ramaze/src/controller/main.rb +0 -8
- data/test/ramaze/src/element/page.rb +0 -17
- data/test/ramaze/src/model.rb +0 -6
- data/test/ramaze/template/index.xhtml +0 -6
- data/test/ramaze/yaml.db +0 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
/*****************************************************************************
|
2
|
+
|
3
|
+
$Id: mapper.h 4529 2007-07-04 11:32:22Z francis $
|
4
|
+
|
5
|
+
File: mapper.h
|
6
|
+
Date: 02Jul07
|
7
|
+
|
8
|
+
Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved.
|
9
|
+
Gmail: garbagecat10
|
10
|
+
|
11
|
+
This program is free software; you can redistribute it and/or modify
|
12
|
+
it under the terms of either: 1) the GNU General Public License
|
13
|
+
as published by the Free Software Foundation; either version 2 of the
|
14
|
+
License, or (at your option) any later version; or 2) Ruby's License.
|
15
|
+
|
16
|
+
See the file COPYING for complete licensing information.
|
17
|
+
|
18
|
+
*****************************************************************************/
|
19
|
+
|
20
|
+
|
21
|
+
#ifndef __Mapper__H_
|
22
|
+
#define __Mapper__H_
|
23
|
+
|
24
|
+
|
25
|
+
/**************
|
26
|
+
class Mapper_t
|
27
|
+
**************/
|
28
|
+
|
29
|
+
class Mapper_t
|
30
|
+
{
|
31
|
+
public:
|
32
|
+
Mapper_t (const string&);
|
33
|
+
virtual ~Mapper_t();
|
34
|
+
|
35
|
+
const char *GetChunk (unsigned);
|
36
|
+
void Close();
|
37
|
+
size_t GetFileSize() {return FileSize;}
|
38
|
+
|
39
|
+
private:
|
40
|
+
size_t FileSize;
|
41
|
+
|
42
|
+
#ifdef OS_UNIX
|
43
|
+
private:
|
44
|
+
int Fd;
|
45
|
+
const char *MapPoint;
|
46
|
+
#endif // OS_UNIX
|
47
|
+
|
48
|
+
#ifdef OS_WIN32
|
49
|
+
private:
|
50
|
+
HANDLE hFile;
|
51
|
+
HANDLE hMapping;
|
52
|
+
const char *MapPoint;
|
53
|
+
#endif // OS_WIN32
|
54
|
+
|
55
|
+
};
|
56
|
+
|
57
|
+
|
58
|
+
#endif // __Mapper__H_
|
59
|
+
|
@@ -0,0 +1,127 @@
|
|
1
|
+
/*****************************************************************************
|
2
|
+
|
3
|
+
$Id: rubymain.cpp 4529 2007-07-04 11:32:22Z francis $
|
4
|
+
|
5
|
+
File: rubymain.cpp
|
6
|
+
Date: 02Jul07
|
7
|
+
|
8
|
+
Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved.
|
9
|
+
Gmail: garbagecat10
|
10
|
+
|
11
|
+
This program is free software; you can redistribute it and/or modify
|
12
|
+
it under the terms of either: 1) the GNU General Public License
|
13
|
+
as published by the Free Software Foundation; either version 2 of the
|
14
|
+
License, or (at your option) any later version; or 2) Ruby's License.
|
15
|
+
|
16
|
+
See the file COPYING for complete licensing information.
|
17
|
+
|
18
|
+
*****************************************************************************/
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
#include <iostream>
|
23
|
+
#include <stdexcept>
|
24
|
+
using namespace std;
|
25
|
+
|
26
|
+
#include <ruby.h>
|
27
|
+
#include "mapper.h"
|
28
|
+
|
29
|
+
static VALUE EmModule;
|
30
|
+
static VALUE FastFileReader;
|
31
|
+
static VALUE Mapper;
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
/*********
|
36
|
+
mapper_dt
|
37
|
+
*********/
|
38
|
+
|
39
|
+
static void mapper_dt (void *ptr)
|
40
|
+
{
|
41
|
+
if (ptr)
|
42
|
+
delete (Mapper_t*) ptr;
|
43
|
+
}
|
44
|
+
|
45
|
+
/**********
|
46
|
+
mapper_new
|
47
|
+
**********/
|
48
|
+
|
49
|
+
static VALUE mapper_new (VALUE self, VALUE filename)
|
50
|
+
{
|
51
|
+
Mapper_t *m = new Mapper_t (StringValuePtr (filename));
|
52
|
+
if (!m)
|
53
|
+
rb_raise (rb_eException, "No Mapper Object");
|
54
|
+
VALUE v = Data_Wrap_Struct (Mapper, 0, mapper_dt, (void*)m);
|
55
|
+
return v;
|
56
|
+
}
|
57
|
+
|
58
|
+
|
59
|
+
/****************
|
60
|
+
mapper_get_chunk
|
61
|
+
****************/
|
62
|
+
|
63
|
+
static VALUE mapper_get_chunk (VALUE self, VALUE start, VALUE length)
|
64
|
+
{
|
65
|
+
Mapper_t *m = NULL;
|
66
|
+
Data_Get_Struct (self, Mapper_t, m);
|
67
|
+
if (!m)
|
68
|
+
rb_raise (rb_eException, "No Mapper Object");
|
69
|
+
|
70
|
+
// TODO, what if some moron sends us a negative start value?
|
71
|
+
unsigned _start = NUM2INT (start);
|
72
|
+
unsigned _length = NUM2INT (length);
|
73
|
+
if ((_start + _length) > m->GetFileSize())
|
74
|
+
rb_raise (rb_eException, "Mapper Range Error");
|
75
|
+
|
76
|
+
const char *chunk = m->GetChunk (_start);
|
77
|
+
if (!chunk)
|
78
|
+
rb_raise (rb_eException, "No Mapper Chunk");
|
79
|
+
return rb_str_new (chunk, _length);
|
80
|
+
}
|
81
|
+
|
82
|
+
/************
|
83
|
+
mapper_close
|
84
|
+
************/
|
85
|
+
|
86
|
+
static VALUE mapper_close (VALUE self)
|
87
|
+
{
|
88
|
+
Mapper_t *m = NULL;
|
89
|
+
Data_Get_Struct (self, Mapper_t, m);
|
90
|
+
if (!m)
|
91
|
+
rb_raise (rb_eException, "No Mapper Object");
|
92
|
+
m->Close();
|
93
|
+
return Qnil;
|
94
|
+
}
|
95
|
+
|
96
|
+
/***********
|
97
|
+
mapper_size
|
98
|
+
***********/
|
99
|
+
|
100
|
+
static VALUE mapper_size (VALUE self)
|
101
|
+
{
|
102
|
+
Mapper_t *m = NULL;
|
103
|
+
Data_Get_Struct (self, Mapper_t, m);
|
104
|
+
if (!m)
|
105
|
+
rb_raise (rb_eException, "No Mapper Object");
|
106
|
+
return INT2NUM (m->GetFileSize());
|
107
|
+
}
|
108
|
+
|
109
|
+
|
110
|
+
/**********************
|
111
|
+
Init_fastfilereaderext
|
112
|
+
**********************/
|
113
|
+
|
114
|
+
extern "C" void Init_fastfilereaderext()
|
115
|
+
{
|
116
|
+
EmModule = rb_define_module ("EventMachine");
|
117
|
+
FastFileReader = rb_define_class_under (EmModule, "FastFileReader", rb_cObject);
|
118
|
+
Mapper = rb_define_class_under (FastFileReader, "Mapper", rb_cObject);
|
119
|
+
|
120
|
+
rb_define_module_function (Mapper, "new", (VALUE(*)(...))mapper_new, 1);
|
121
|
+
rb_define_method (Mapper, "size", (VALUE(*)(...))mapper_size, 0);
|
122
|
+
rb_define_method (Mapper, "close", (VALUE(*)(...))mapper_close, 0);
|
123
|
+
rb_define_method (Mapper, "get_chunk", (VALUE(*)(...))mapper_get_chunk, 2);
|
124
|
+
}
|
125
|
+
|
126
|
+
|
127
|
+
|
data/external/test_support.rb
CHANGED
@@ -56,3 +56,16 @@ module SwiftcoreTestSupport
|
|
56
56
|
end
|
57
57
|
|
58
58
|
end
|
59
|
+
|
60
|
+
class EMConnectionMock
|
61
|
+
attr_accessor :uri, :name
|
62
|
+
|
63
|
+
def send_data data
|
64
|
+
end
|
65
|
+
|
66
|
+
def send_file_data filename
|
67
|
+
end
|
68
|
+
|
69
|
+
def close_connection; end
|
70
|
+
def close_connection_after_writing; end
|
71
|
+
end
|
data/setup.rb
CHANGED
@@ -13,6 +13,11 @@ Dir.chdir(basedir)
|
|
13
13
|
Package.setup("1.0") {
|
14
14
|
name "Swiftcore Swiftiply"
|
15
15
|
|
16
|
+
build_ext "fastfilereader"
|
17
|
+
translate(:ext, 'ext/fastfilereader/' => '/')
|
18
|
+
#translate(:ext, 'ext/http11/' => 'iowa/')
|
19
|
+
ext "ext/fastfilereader/fastfilereaderext.so"
|
20
|
+
|
16
21
|
translate(:lib, 'src/' => '')
|
17
22
|
translate(:bin, 'bin/' => '')
|
18
23
|
lib(*Dir["src/swiftcore/**/*.rb"])
|
@@ -25,7 +30,7 @@ Package.setup("1.0") {
|
|
25
30
|
#File.rename("#{Config::CONFIG["bindir"]}/mongrel_rails","#{Config::CONFIG["bindir"]}/mongrel_rails.orig")
|
26
31
|
bin "bin/mongrel_rails"
|
27
32
|
|
28
|
-
|
29
|
-
|
33
|
+
unit_test "test/TC_ProxyBag.rb"
|
34
|
+
unit_test "test/TC_Swiftiply.rb"
|
30
35
|
true
|
31
36
|
}
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# Written by Francis Cianfrocca (garbagecat10@gmail.com) with contributions
|
2
|
+
# from Kirk Haines (wyhaines@gmail.com).
|
3
|
+
|
4
|
+
begin
|
5
|
+
load_attempted ||= false
|
6
|
+
require 'eventmachine'
|
7
|
+
require 'fastfilereaderext'
|
8
|
+
rescue LoadError => e
|
9
|
+
unless load_attempted
|
10
|
+
load_attempted = true
|
11
|
+
require 'eventmachine'
|
12
|
+
require 'fastfilereaderext'
|
13
|
+
retry
|
14
|
+
end
|
15
|
+
raise e
|
16
|
+
end
|
17
|
+
|
18
|
+
module EventMachine
|
19
|
+
class FastFileReader
|
20
|
+
include EventMachine::Deferrable
|
21
|
+
|
22
|
+
attr_reader :size
|
23
|
+
|
24
|
+
# TODO, make these constants tunable parameters
|
25
|
+
ChunkSize = 16384
|
26
|
+
BackpressureLevel = 50000
|
27
|
+
MappingThreshold = 32768
|
28
|
+
Crn = "\r\n".freeze
|
29
|
+
C0rnrn = "0\r\n\r\n".freeze
|
30
|
+
|
31
|
+
class << self
|
32
|
+
# Return a newly-created instance of this class, or nil on error.
|
33
|
+
#
|
34
|
+
def open filename
|
35
|
+
FastFileReader.new(filename)
|
36
|
+
rescue
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
# This constructor can throw exceptions. Use #open to avoid that fate.
|
43
|
+
#
|
44
|
+
def initialize filename
|
45
|
+
# Throw an exception if we can't open the file.
|
46
|
+
# TODO, perhaps we should throw a different exception?
|
47
|
+
raise "no file" unless File.exist?(filename)
|
48
|
+
|
49
|
+
@size = File.size?(filename)
|
50
|
+
if @size >= MappingThreshold
|
51
|
+
@mapping = Mapper.new( filename )
|
52
|
+
else
|
53
|
+
@content = File.read( filename )
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
# This is a no-op for small files that have a @content
|
59
|
+
# member. For large files with a @mapping, we call #close
|
60
|
+
# on the mapping. In general, this will be done by the
|
61
|
+
# finalizer when the GC runs, but there will be cases
|
62
|
+
# when we will want to know that the underlying file mapping
|
63
|
+
# is closed. This matters particularly on Windows, because
|
64
|
+
# we're holding some HANDLE objects open that can cause
|
65
|
+
# trouble for Ruby.
|
66
|
+
def close
|
67
|
+
@mapping.close if @mapping
|
68
|
+
end
|
69
|
+
|
70
|
+
# We expect to receive something like an EventMachine::Connection object.
|
71
|
+
# We also expect to be running inside a reaactor loop, because we call
|
72
|
+
# EventMachine#next_tick when we have too much data to send.
|
73
|
+
def stream_as_http_chunks sink
|
74
|
+
if @content
|
75
|
+
if @content.length > 0
|
76
|
+
sink.send_data( "#{@content.length.to_s(16)}\r\n#{@content}#{Crn}" )
|
77
|
+
end
|
78
|
+
sink.send_data( C0rnrn )
|
79
|
+
set_deferred_success
|
80
|
+
else
|
81
|
+
@position = 0
|
82
|
+
@sink = sink
|
83
|
+
stream_one_http_chunk
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def stream_one_http_chunk
|
88
|
+
loop {
|
89
|
+
if @position < @size
|
90
|
+
if @sink.get_outbound_data_size > BackpressureLevel
|
91
|
+
EventMachine::next_tick {stream_one_http_chunk}
|
92
|
+
break
|
93
|
+
else
|
94
|
+
len = @size - @position
|
95
|
+
len = ChunkSize if (len > ChunkSize)
|
96
|
+
@sink.send_data( "#{len.to_s(16)}\r\n#{@mapping.get_chunk( @position, len))}\r\n" )
|
97
|
+
@position += len
|
98
|
+
end
|
99
|
+
else
|
100
|
+
@sink.send_data( C0rnrn )
|
101
|
+
set_deferred_success
|
102
|
+
break
|
103
|
+
end
|
104
|
+
}
|
105
|
+
end
|
106
|
+
private :stream_one_http_chunk
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
data/src/swiftcore/Swiftiply.rb
CHANGED
@@ -2,33 +2,66 @@ begin
|
|
2
2
|
load_attempted ||= false
|
3
3
|
require 'digest/sha2'
|
4
4
|
require 'eventmachine'
|
5
|
+
require 'fastfilereaderext'
|
6
|
+
require 'swiftcore/types'
|
5
7
|
rescue LoadError => e
|
6
8
|
unless load_attempted
|
7
9
|
load_attempted = true
|
10
|
+
# Ugh. Everything gets slower once rubygems are used. So, for the
|
11
|
+
# best speed possible, don't install EventMachine or Swiftiply via
|
12
|
+
# gems.
|
8
13
|
require 'rubygems'
|
14
|
+
retry
|
9
15
|
end
|
10
16
|
raise e
|
11
17
|
end
|
12
18
|
|
13
19
|
module Swiftcore
|
14
20
|
module Swiftiply
|
15
|
-
Version = '0.
|
16
|
-
|
21
|
+
Version = '0.6.0'
|
22
|
+
|
23
|
+
# Yeah, these constants look kind of tacky. Inside of tight loops,
|
24
|
+
# though, using them makes a small but measurable difference, and those
|
25
|
+
# small differences add up....
|
26
|
+
C_empty = ''.freeze
|
27
|
+
C_slash = '/'.freeze
|
28
|
+
C_slashindex_html = '/index.html'.freeze
|
29
|
+
Caos = 'application/octet-stream'.freeze
|
30
|
+
Ccache_directory = 'cache_directory'.freeze
|
31
|
+
Ccache_extensions = 'cache_extensions'.freeze
|
17
32
|
Ccluster_address = 'cluster_address'.freeze
|
18
33
|
Ccluster_port = 'cluster_port'.freeze
|
34
|
+
Ccluster_server = 'cluster_server'.freeze
|
19
35
|
CBackendAddress = 'BackendAddress'.freeze
|
20
36
|
CBackendPort = 'BackendPort'.freeze
|
21
|
-
|
22
|
-
Cincoming = 'incoming'.freeze
|
23
|
-
Ckeepalive = 'keepalive'.freeze
|
37
|
+
Cchunked_encoding_threshold = 'chunked_encoding_threshold'.freeze
|
24
38
|
Cdaemonize = 'daemonize'.freeze
|
25
|
-
|
39
|
+
Cdefault = 'default'.freeze
|
40
|
+
Cdocroot = 'docroot'.freeze
|
41
|
+
Cepoll = 'epoll'.freeze
|
42
|
+
Cepoll_descriptors = 'epoll_descriptors'.freeze
|
43
|
+
Cgroup = 'group'.freeze
|
26
44
|
Chost = 'host'.freeze
|
27
|
-
|
45
|
+
Cincoming = 'incoming'.freeze
|
46
|
+
Ckeepalive = 'keepalive'.freeze
|
47
|
+
Ckey = 'key'.freeze
|
48
|
+
Cmap = 'map'.freeze
|
49
|
+
Cmsg_expired = 'browser connection expired'.freeze
|
28
50
|
Coutgoing = 'outgoing'.freeze
|
51
|
+
Cport = 'port'.freeze
|
52
|
+
Credeployable = 'redeployable'.freeze
|
53
|
+
Credeployment_sizelimit = 'redeployment_sizelimit'.freeze
|
54
|
+
Cswiftclient = 'swiftclient'.freeze
|
29
55
|
Ctimeout = 'timeout'.freeze
|
30
|
-
|
56
|
+
Curl = 'url'.freeze
|
57
|
+
Cuser = 'user'.freeze
|
58
|
+
|
59
|
+
C_fsep = File::SEPARATOR
|
60
|
+
|
61
|
+
RunningConfig = {}
|
31
62
|
|
63
|
+
class EMStartServerError < RuntimeError; end
|
64
|
+
|
32
65
|
# The ProxyBag is a class that holds the client and the server queues,
|
33
66
|
# and that is responsible for managing them, matching them, and expiring
|
34
67
|
# them, if necessary.
|
@@ -41,7 +74,14 @@ module Swiftcore
|
|
41
74
|
@id_map = {}
|
42
75
|
@reverse_id_map = {}
|
43
76
|
@incoming_map = {}
|
77
|
+
@docroot_map = {}
|
78
|
+
@log_map = {}
|
79
|
+
@redeployable_map = {}
|
80
|
+
@keys = {}
|
44
81
|
@demanding_clients = Hash.new {|h,k| h[k] = []}
|
82
|
+
@hitcounters = Hash.new {|h,k| h[k] = 0}
|
83
|
+
# Kids, don't do this at home. It's gross.
|
84
|
+
@typer = MIME::Types.instance_variable_get('@__types__')
|
45
85
|
|
46
86
|
class << self
|
47
87
|
|
@@ -53,14 +93,12 @@ module Swiftcore
|
|
53
93
|
# connections must send the correct access key before being added to
|
54
94
|
# the cluster as a valid backend.
|
55
95
|
|
56
|
-
def
|
57
|
-
@
|
96
|
+
def get_key(h)
|
97
|
+
@keys[h] || C_empty
|
58
98
|
end
|
59
99
|
|
60
|
-
|
61
|
-
|
62
|
-
def key=(val)
|
63
|
-
@key = val
|
100
|
+
def set_key(h,val)
|
101
|
+
@keys[h] = val
|
64
102
|
end
|
65
103
|
|
66
104
|
def add_id(who,what)
|
@@ -73,10 +111,41 @@ module Swiftcore
|
|
73
111
|
@reverse_id_map.delete(what)
|
74
112
|
end
|
75
113
|
|
114
|
+
def incoming_mapping(name)
|
115
|
+
@incoming_map[name]
|
116
|
+
end
|
117
|
+
|
76
118
|
def add_incoming_mapping(hashcode,name)
|
77
119
|
@incoming_map[name] = hashcode
|
78
120
|
end
|
121
|
+
|
122
|
+
def remove_incoming_mapping(name)
|
123
|
+
@incoming_map.delete(name)
|
124
|
+
end
|
125
|
+
|
126
|
+
def add_incoming_docroot(path,name)
|
127
|
+
@docroot_map[name] = path
|
128
|
+
end
|
129
|
+
|
130
|
+
def remove_incoming_docroot(name)
|
131
|
+
@docroot_map.delete(name)
|
132
|
+
end
|
133
|
+
|
134
|
+
def add_incoming_redeployable(limit,name)
|
135
|
+
@redeployable_map[name] = limit
|
136
|
+
end
|
79
137
|
|
138
|
+
def remove_incoming_redeployable(name)
|
139
|
+
@redeployable_map.delete(name)
|
140
|
+
end
|
141
|
+
|
142
|
+
def add_log(log,name)
|
143
|
+
@log_map[name] = log
|
144
|
+
end
|
145
|
+
|
146
|
+
# Sets the default proxy destination, if requests are received
|
147
|
+
# which do not match a defined destination.
|
148
|
+
|
80
149
|
def default_name
|
81
150
|
@default_name
|
82
151
|
end
|
@@ -86,29 +155,127 @@ module Swiftcore
|
|
86
155
|
end
|
87
156
|
|
88
157
|
# This timeout is the amount of time a connection will sit in queue
|
89
|
-
# waiting for a backend to process it.
|
158
|
+
# waiting for a backend to process it. A client connection that
|
159
|
+
# sits for longer than this timeout receives a 503 response and
|
160
|
+
# is dropped.
|
90
161
|
|
91
162
|
def server_unavailable_timeout
|
92
163
|
@server_unavailable_timeout
|
93
164
|
end
|
94
165
|
|
95
|
-
# Sets the server unavailable timeout value.
|
96
|
-
|
97
166
|
def server_unavailable_timeout=(val)
|
98
167
|
@server_unavailable_timeout = val
|
99
168
|
end
|
100
169
|
|
170
|
+
# The chunked_encoding_threshold is a file size limit. Files
|
171
|
+
# which fall below this limit are sent in one chunk of data.
|
172
|
+
# Files which hit or exceed this limit are delivered via chunked
|
173
|
+
# encoding.
|
174
|
+
|
175
|
+
def chunked_encoding_threshold
|
176
|
+
@chunked_enconding_threshold
|
177
|
+
end
|
178
|
+
|
179
|
+
def chunked_encoding_threshold=(val)
|
180
|
+
@chunked_encoding_threshold = val
|
181
|
+
end
|
182
|
+
|
183
|
+
# Handle static files. It employs an extension to efficiently
|
184
|
+
# handle large files, and depends on an addition to
|
185
|
+
# EventMachine, send_file_data(), to efficiently handle small
|
186
|
+
# files. In my tests, it streams in excess of 120 megabytes of
|
187
|
+
# data per second for large files, and does 8000+ to 9000+
|
188
|
+
# requests per second with small files (i.e. under 4k). I think
|
189
|
+
# this can still be improved upon for small files.
|
190
|
+
#
|
191
|
+
# Todo for 0.7.0 -- add etag/if-modified/if-modified-since
|
192
|
+
# support.
|
193
|
+
#
|
194
|
+
# TODO: Add support for logging static file delivery if wanted.
|
195
|
+
# The ideal logging would probably be to Analogger since it'd
|
196
|
+
# limit the performance impact of the the logging.
|
197
|
+
#
|
198
|
+
|
199
|
+
def serve_static_file(clnt)
|
200
|
+
path_info = clnt.uri
|
201
|
+
client_name = clnt.name
|
202
|
+
dr = @docroot_map[client_name]
|
203
|
+
if path = find_static_file(dr,path_info,client_name)
|
204
|
+
#ct = ::MIME::Types.type_for(path).first || Caos
|
205
|
+
ct = @typer.simple_type_for(path) || Caos
|
206
|
+
fsize = File.size?(path)
|
207
|
+
if fsize > @chunked_encoding_threshold
|
208
|
+
clnt.send_data "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: #{ct}\r\nTransfer-encoding: chunked\r\n\r\n"
|
209
|
+
EM::Deferrable.future(clnt.stream_file_data(path, :http_chunks=>true)) {clnt.close_connection_after_writing}
|
210
|
+
else
|
211
|
+
clnt.send_data "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: #{ct}\r\nContent-length: #{fsize}\r\n\r\n"
|
212
|
+
clnt.send_file_data path
|
213
|
+
clnt.close_connection_after_writing
|
214
|
+
end
|
215
|
+
true
|
216
|
+
else
|
217
|
+
false
|
218
|
+
end
|
219
|
+
# The exception is going to be eaten here, because some
|
220
|
+
# dumb file IO error shouldn't take Swiftiply down.
|
221
|
+
# TODO: It should log these errors, though.
|
222
|
+
rescue Object
|
223
|
+
clnt.close_connection_after_writing
|
224
|
+
false
|
225
|
+
end
|
226
|
+
|
227
|
+
# Determine if the requested file, in the given docroot, exists
|
228
|
+
# and is a file (i.e. not a directory).
|
229
|
+
#
|
230
|
+
# If Rails style page caching is enabled, this method will be
|
231
|
+
# dynamically replaced by a more sophisticated version.
|
232
|
+
|
233
|
+
def find_static_file(docroot,path_info,client_name)
|
234
|
+
path = File.join(docroot,path_info)
|
235
|
+
path if FileTest.exist?(path) and FileTest.file?(path)
|
236
|
+
end
|
237
|
+
|
101
238
|
# Pushes a front end client (web browser) into the queue of clients
|
102
239
|
# waiting to be serviced if there's no server available to handle
|
103
240
|
# it right now.
|
104
241
|
|
105
|
-
def add_frontend_client
|
242
|
+
def add_frontend_client(clnt,data_q,data)
|
106
243
|
clnt.create_time = @ctime
|
244
|
+
clnt.data_pos = clnt.data_len = 0 if clnt.redeployable = @redeployable_map[clnt.name]
|
245
|
+
|
246
|
+
unless @docroot_map.has_key?(clnt.name) and serve_static_file(clnt)
|
247
|
+
data_q.unshift data
|
248
|
+
unless match_client_to_server_now(clnt)
|
249
|
+
if clnt.uri =~ /\w+-\w+-\w+\.\w+\.[\w\.]+-(\w+)?$/
|
250
|
+
# NOTE: I hate using unshift and delete on arrays
|
251
|
+
# in this code. Look at switching to something
|
252
|
+
# with a faster profile for push/pull from both
|
253
|
+
# ends as well as deletes. There has to be
|
254
|
+
# something. A linked list solves the push/pull
|
255
|
+
# which might be good enough.
|
256
|
+
@demanding_clients[$1].unshift clnt
|
257
|
+
else
|
258
|
+
@client_q[@incoming_map[clnt.name]].unshift(clnt)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
#clnt.push ## wasted call, yes?
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def rebind_frontend_client(clnt)
|
266
|
+
clnt.create_time = @ctime
|
267
|
+
clnt.data_pos = clnt.data_len = 0
|
268
|
+
|
107
269
|
unless match_client_to_server_now(clnt)
|
108
270
|
if clnt.uri =~ /\w+-\w+-\w+\.\w+\.[\w\.]+-(\w+)?$/
|
271
|
+
# NOTE: I hate using unshift and delete on arrays
|
272
|
+
# in this code. Look at switching to something
|
273
|
+
# with a faster profile for push/pull from both
|
274
|
+
# ends as well as deletes. There has to be
|
275
|
+
# something.
|
109
276
|
@demanding_clients[$1].unshift clnt
|
110
277
|
else
|
111
|
-
@client_q[clnt.name].unshift(clnt)
|
278
|
+
@client_q[@incoming_map[clnt.name]].unshift(clnt)
|
112
279
|
end
|
113
280
|
end
|
114
281
|
end
|
@@ -127,38 +294,28 @@ module Swiftcore
|
|
127
294
|
end
|
128
295
|
|
129
296
|
# Removes the named client from the client queue.
|
130
|
-
# TODO: Try replacing this with
|
131
|
-
# here has to
|
132
|
-
|
297
|
+
# TODO: Try replacing this with ...something. Performance
|
298
|
+
# here has to be bad when the list is long.
|
299
|
+
|
133
300
|
def remove_client clnt
|
134
301
|
@client_q[clnt.name].delete clnt
|
135
302
|
end
|
136
303
|
|
137
|
-
#
|
138
|
-
# waiting clients with waiting servers until the queue
|
139
|
-
# runs out of one or the other. DEPRECATED
|
140
|
-
|
141
|
-
#def match_clients_to_servers
|
142
|
-
# while @server_q.first && @client_q.first
|
143
|
-
# server = @server_q.pop
|
144
|
-
# client = @client_q.pop
|
145
|
-
# server.associate = client
|
146
|
-
# client.associate = server
|
147
|
-
# client.push
|
148
|
-
# end
|
149
|
-
#end
|
150
|
-
|
151
|
-
# Tries to match the client passed as an argument to a
|
304
|
+
# Tries to match the client (passed as an argument) to a
|
152
305
|
# server.
|
153
306
|
|
154
307
|
def match_client_to_server_now(client)
|
155
308
|
sq = @server_q[@incoming_map[client.name]]
|
156
|
-
if client.uri =~ /\w+-\w+-\w+\.\w+\.[\w\.]+-(\w+)?$/
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
309
|
+
if client.uri =~ /\w+-\w+-\w+\.\w+\.[\w\.]+-(\w+)?$/
|
310
|
+
if sidx = sq.index(@reverse_id_map[$1])
|
311
|
+
server = sq.delete_at(sidx)
|
312
|
+
server.associate = client
|
313
|
+
client.associate = server
|
314
|
+
client.push
|
315
|
+
true
|
316
|
+
else
|
317
|
+
false
|
318
|
+
end
|
162
319
|
elsif server = sq.pop
|
163
320
|
server.associate = client
|
164
321
|
client.associate = server
|
@@ -169,7 +326,7 @@ module Swiftcore
|
|
169
326
|
end
|
170
327
|
end
|
171
328
|
|
172
|
-
# Tries to match the server passed as an argument to a
|
329
|
+
# Tries to match the server (passed as an argument) to a
|
173
330
|
# client.
|
174
331
|
|
175
332
|
def match_server_to_client_now(server)
|
@@ -192,10 +349,13 @@ module Swiftcore
|
|
192
349
|
# available to process clients and expire any clients that
|
193
350
|
# have been waiting longer than @server_unavailable_timeout
|
194
351
|
# seconds. Clients which are expired will receive a 503
|
195
|
-
# response.
|
352
|
+
# response. If this is happening, either you need more
|
353
|
+
# backend processes, or you @server_unavailable_timeout is
|
354
|
+
# too short.
|
196
355
|
|
197
356
|
def expire_clients
|
198
357
|
now = Time.now
|
358
|
+
|
199
359
|
@server_q.each_key do |name|
|
200
360
|
unless @server_q[name].first
|
201
361
|
while c = @client_q[name].pop
|
@@ -225,12 +385,10 @@ module Swiftcore
|
|
225
385
|
|
226
386
|
class ClusterProtocol < EventMachine::Connection
|
227
387
|
|
228
|
-
attr_accessor :create_time, :associate, :name
|
388
|
+
attr_accessor :create_time, :associate, :name, :redeployable, :data_pos, :data_len
|
229
389
|
|
230
390
|
Crn = "\r\n".freeze
|
231
391
|
Crnrn = "\r\n\r\n".freeze
|
232
|
-
Rrnrn = /\r\n\r\n/
|
233
|
-
R_colon = /:/
|
234
392
|
C_blank = ''.freeze
|
235
393
|
|
236
394
|
# Initialize the @data array, which is the temporary storage for blocks
|
@@ -239,26 +397,28 @@ module Swiftcore
|
|
239
397
|
|
240
398
|
def initialize *args
|
241
399
|
@data = []
|
242
|
-
@
|
243
|
-
@uri = nil
|
400
|
+
@data_pos = 0
|
401
|
+
@name = @uri = nil
|
244
402
|
super
|
245
403
|
end
|
246
404
|
|
247
405
|
def receive_data data
|
248
|
-
@data.unshift data
|
249
406
|
if @name
|
407
|
+
@data.unshift data
|
250
408
|
push
|
251
409
|
else
|
252
|
-
data =~
|
253
|
-
|
254
|
-
|
255
|
-
@name = $1
|
256
|
-
|
257
|
-
|
258
|
-
|
410
|
+
if data =~ /^Host:\s*([^\r:]*)/
|
411
|
+
# NOTE: Should I be using intern for this? It might not
|
412
|
+
# be a good idea.
|
413
|
+
@name = $1.intern
|
414
|
+
|
415
|
+
data =~ /\s([^\s\?]*)/
|
416
|
+
@uri = $1
|
417
|
+
@name = ProxyBag.default_name unless ProxyBag.incoming_mapping(@name)
|
418
|
+
ProxyBag.add_frontend_client(self,@data,data)
|
419
|
+
elsif data =~ /\r\n\r\n/
|
259
420
|
@name = ProxyBag.default_name
|
260
|
-
ProxyBag.add_frontend_client
|
261
|
-
push
|
421
|
+
ProxyBag.add_frontend_client(self,@data,data)
|
262
422
|
end
|
263
423
|
end
|
264
424
|
end
|
@@ -281,8 +441,26 @@ module Swiftcore
|
|
281
441
|
|
282
442
|
def push
|
283
443
|
if @associate
|
284
|
-
|
285
|
-
|
444
|
+
unless @redeployable
|
445
|
+
# normal data push
|
446
|
+
data = nil
|
447
|
+
@associate.send_data data while data = @data.pop
|
448
|
+
else
|
449
|
+
# redeployable data push; just send the stuff that has
|
450
|
+
# not already been sent.
|
451
|
+
(@data.length - 1 - @data_pos).downto(0) do |p|
|
452
|
+
d = @data[p]
|
453
|
+
@associate.send_data d
|
454
|
+
@data_len += d.length
|
455
|
+
end
|
456
|
+
@data_pos = @data.length
|
457
|
+
|
458
|
+
# If the request size crosses the size limit, then
|
459
|
+
# disallow redeployent of this request.
|
460
|
+
if @data_len > @redeployable
|
461
|
+
@redeployable = false
|
462
|
+
@data.clear
|
463
|
+
end
|
286
464
|
end
|
287
465
|
end
|
288
466
|
end
|
@@ -301,8 +479,11 @@ module Swiftcore
|
|
301
479
|
@uri
|
302
480
|
end
|
303
481
|
|
304
|
-
|
482
|
+
def setup_for_redeployment
|
483
|
+
@data_pos = 0
|
484
|
+
end
|
305
485
|
|
486
|
+
end
|
306
487
|
|
307
488
|
# The BackendProtocol is the EventMachine::Connection subclass that
|
308
489
|
# handles the communications between Swiftiply and the backend process
|
@@ -311,8 +492,8 @@ module Swiftcore
|
|
311
492
|
class BackendProtocol < EventMachine::Connection
|
312
493
|
attr_accessor :associate, :id
|
313
494
|
|
495
|
+
C0rnrn = "0\r\n\r\n".freeze
|
314
496
|
Crnrn = "\r\n\r\n".freeze
|
315
|
-
Rrnrn = /\r\n\r\n/
|
316
497
|
|
317
498
|
def initialize *args
|
318
499
|
@name = self.class.bname
|
@@ -336,31 +517,47 @@ module Swiftcore
|
|
336
517
|
def setup
|
337
518
|
@headers = ''
|
338
519
|
@headers_completed = false
|
339
|
-
|
520
|
+
#@content_length = nil
|
340
521
|
@content_sent = 0
|
341
522
|
end
|
342
|
-
|
523
|
+
|
343
524
|
# Receive data from the backend process. Headers are parsed from
|
344
|
-
# the rest of the content
|
345
|
-
#
|
346
|
-
#
|
347
|
-
#
|
525
|
+
# the rest of the content. If a Content-Length header is present,
|
526
|
+
# that is used to determine how much data to expect. Otherwise,
|
527
|
+
# if 'Transfer-encoding: chunked' is present, assume chunked
|
528
|
+
# encoding. Otherwise be paranoid; something isn't the way we like
|
529
|
+
# it to be.
|
348
530
|
|
349
531
|
def receive_data data
|
350
532
|
unless @initialized
|
351
|
-
|
352
|
-
|
353
|
-
|
533
|
+
preamble = data.slice!(0..24)
|
534
|
+
|
535
|
+
keylen = preamble[23..24].to_i(16)
|
536
|
+
keylen = 0 if keylen < 0
|
537
|
+
key = keylen > 0 ? data.slice!(0..(keylen - 1)) : C_empty
|
538
|
+
if preamble[0..10] == Cswiftclient and key == ProxyBag.get_key(@name)
|
539
|
+
@id = preamble[11..22]
|
540
|
+
ProxyBag.add_id(self,@id)
|
541
|
+
@initialized = true
|
542
|
+
else
|
543
|
+
close_connection
|
544
|
+
return
|
545
|
+
end
|
354
546
|
end
|
547
|
+
|
355
548
|
unless @headers_completed
|
356
|
-
if data
|
549
|
+
if data =~ /\r\n\r\n/
|
357
550
|
@headers_completed = true
|
358
|
-
h,
|
551
|
+
h,data = data.split(/\r\n\r\n/,2)
|
359
552
|
@headers << h << Crnrn
|
360
|
-
@headers =~ /Content-
|
361
|
-
|
553
|
+
if @headers =~ /Content-[Ll]ength:\s*([^\r]+)/
|
554
|
+
@content_length = $1.to_i
|
555
|
+
elsif @headers =~ /Transfer-encoding:\s*chunked/
|
556
|
+
@content_length = nil
|
557
|
+
else
|
558
|
+
@content_length = 0
|
559
|
+
end
|
362
560
|
@associate.send_data @headers
|
363
|
-
data = d
|
364
561
|
else
|
365
562
|
@headers << data
|
366
563
|
end
|
@@ -369,15 +566,20 @@ module Swiftcore
|
|
369
566
|
if @headers_completed
|
370
567
|
@associate.send_data data
|
371
568
|
@content_sent += data.length
|
372
|
-
if @content_sent >= @content_length
|
569
|
+
if @content_length and @content_sent >= @content_length or data[-6..-1] == C0rnrn
|
373
570
|
@associate.close_connection_after_writing
|
374
571
|
@associate = nil
|
375
|
-
|
572
|
+
@headers = ''
|
573
|
+
@headers_completed = false
|
574
|
+
#@content_length = nil
|
575
|
+
@content_sent = 0
|
576
|
+
#setup
|
376
577
|
ProxyBag.add_server self
|
377
578
|
end
|
378
579
|
end
|
580
|
+
# TODO: Log these errors!
|
379
581
|
rescue
|
380
|
-
@associate.close_connection_after_writing
|
582
|
+
@associate.close_connection_after_writing if @associate
|
381
583
|
@associate = nil
|
382
584
|
setup
|
383
585
|
ProxyBag.add_server self
|
@@ -390,7 +592,13 @@ module Swiftcore
|
|
390
592
|
|
391
593
|
def unbind
|
392
594
|
if @associate
|
393
|
-
|
595
|
+
if !@associate.redeployable or @content_length
|
596
|
+
@associate.close_connection_after_writing
|
597
|
+
else
|
598
|
+
@associate.associate = nil
|
599
|
+
@associate.setup_for_redeployment
|
600
|
+
ProxyBag.rebind_frontend_client(@associate)
|
601
|
+
end
|
394
602
|
else
|
395
603
|
ProxyBag.remove_server(self)
|
396
604
|
end
|
@@ -410,36 +618,157 @@ module Swiftcore
|
|
410
618
|
# handlers, then create the timers that are used to expire unserviced
|
411
619
|
# clients and to update the Proxy's clock.
|
412
620
|
|
413
|
-
def self.run(config
|
414
|
-
existing_backends = {}
|
621
|
+
def self.run(config)
|
622
|
+
@existing_backends = {}
|
623
|
+
|
624
|
+
# Default is to assume we want to try to turn epoll support on. EM
|
625
|
+
# ignores this on platforms that don't support it, so this is safe.
|
626
|
+
EventMachine.epoll unless config.has_key?(Cepoll) and !config[Cepoll]
|
627
|
+
EventMachine.set_descriptor_table_size(4096 || config[Cepoll_descriptors]) if config[Cepoll]
|
415
628
|
EventMachine.run do
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
629
|
+
trap("HUP") {em_config(Swiftcore::SwiftiplyExec.parse_options); GC.start}
|
630
|
+
trap("INT") {EventMachine.stop_event_loop}
|
631
|
+
em_config(config)
|
632
|
+
GC.start
|
633
|
+
end
|
634
|
+
end
|
635
|
+
|
636
|
+
def self.em_config(config)
|
637
|
+
new_config = {}
|
638
|
+
if RunningConfig[Ccluster_address] != config[Ccluster_address] or RunningConfig[Ccluster_port] != config[Ccluster_port]
|
639
|
+
begin
|
640
|
+
new_config[Ccluster_server] = EventMachine.start_server(
|
641
|
+
config[Ccluster_address],
|
642
|
+
config[Ccluster_port],
|
643
|
+
ClusterProtocol)
|
644
|
+
rescue RuntimeError => e
|
645
|
+
advice = ''
|
646
|
+
if config[Ccluster_port] < 1024
|
647
|
+
advice << 'Make sure you have the correct permissions to use that port, and make sure there is nothing else running on that port.'
|
648
|
+
else
|
649
|
+
advice << 'Make sure there is nothing else running on that port.'
|
650
|
+
end
|
651
|
+
advice << " The original error was: #{e}\n"
|
652
|
+
raise EMStartServerError.new("The listener on #{config[Ccluster_address]}:#{config[Ccluster_port]} could not be started.\n#{advice}")
|
653
|
+
end
|
654
|
+
new_config[Ccluster_address] = config[Ccluster_address]
|
655
|
+
new_config[Ccluster_port] = config[Ccluster_port]
|
656
|
+
RunningConfig[Ccluster_server].stop_server if RunningConfig.has_key?(Ccluster_server)
|
657
|
+
else
|
658
|
+
new_config[Ccluster_server] = RunningConfig[Ccluster_server]
|
659
|
+
new_config[Ccluster_address] = RunningConfig[Ccluster_address]
|
660
|
+
new_config[Ccluster_port] = RunningConfig[Ccluster_port]
|
661
|
+
end
|
662
|
+
|
663
|
+
new_config[Coutgoing] = {}
|
664
|
+
|
665
|
+
config[Cmap].each do |m|
|
666
|
+
if m[Ckeepalive]
|
667
|
+
# keepalive requests are standard Swiftiply requests.
|
668
|
+
|
669
|
+
# The hash of the "outgoing" config section. It is used to
|
670
|
+
# uniquely identify a section.
|
671
|
+
hash = Digest::SHA256.hexdigest(m[Cincoming].sort.join('|')).intern
|
672
|
+
|
673
|
+
# For each incoming entry, do setup.
|
674
|
+
new_config[Cincoming] = {}
|
675
|
+
m[Cincoming].each do |p_|
|
676
|
+
p = p_.intern
|
677
|
+
new_config[Cincoming][p] = {}
|
678
|
+
ProxyBag.add_incoming_mapping(hash,p)
|
679
|
+
|
680
|
+
if m.has_key?(Cdocroot)
|
681
|
+
ProxyBag.add_incoming_docroot(m[Cdocroot],p)
|
682
|
+
else
|
683
|
+
ProxyBag.remove_incoming_docroot(p)
|
684
|
+
end
|
685
|
+
|
686
|
+
if m[Credeployable]
|
687
|
+
ProxyBag.add_incoming_redeployable(m[Credeployment_sizelimit] || 16384,p)
|
688
|
+
else
|
689
|
+
ProxyBag.remove_incoming_redeployable(p)
|
690
|
+
end
|
691
|
+
|
692
|
+
if m.has_key?(Ckey)
|
693
|
+
ProxyBag.set_key(hash,m[Ckey])
|
694
|
+
else
|
695
|
+
ProxyBag.set_key(hash,C_empty)
|
696
|
+
end
|
697
|
+
|
698
|
+
if m.has_key?(Ccache_extensions) or m.has_key?(Ccache_directory)
|
699
|
+
require 'swiftcore/Swiftiply/support_pagecache'
|
700
|
+
ProxyBag.add_suffix_list((m[Ccache_extensions] || ProxyBag.const_get(:DefaultSuffixes)),p)
|
701
|
+
ProxyBag.add_cache_dir((m[Ccache_directory] || ProxyBag.const_get(:DefaultCacheDir)),p)
|
702
|
+
else
|
703
|
+
ProxyBag.remove_suffix_list(p) if ProxyBag.respond_to?(:remove_suffix_list)
|
704
|
+
ProxyBag.remove_cache_dir(p) if ProxyBag.respond_to?(:remove_cache_dir)
|
705
|
+
end
|
706
|
+
|
707
|
+
m[Coutgoing].each do |o|
|
708
|
+
ProxyBag.default_name = p if m[Cdefault]
|
709
|
+
if @existing_backends.has_key?(o)
|
710
|
+
new_config[Coutgoing][o] ||= RunningConfig[Coutgoing][o]
|
711
|
+
next
|
712
|
+
else
|
713
|
+
@existing_backends[o] = true
|
714
|
+
backend_class = Class.new(BackendProtocol)
|
715
|
+
backend_class.bname = hash
|
716
|
+
host, port = o.split(/:/,2)
|
717
|
+
begin
|
718
|
+
new_config[Coutgoing][o] = EventMachine.start_server(host, port.to_i, backend_class)
|
719
|
+
rescue RuntimeError => e
|
720
|
+
advice = ''
|
721
|
+
if port.to_i < 1024
|
722
|
+
advice << 'Make sure you have the correct permissions to use that port, and make sure there is nothing else running on that port.'
|
723
|
+
else
|
724
|
+
advice << 'Make sure there is nothing else running on that port.'
|
725
|
+
end
|
726
|
+
advice << " The original error was: #{e}\n"
|
727
|
+
raise EMStartServerError.new("The listener on #{host}:#{port} could not be started.\n#{advice}")
|
433
728
|
end
|
434
729
|
end
|
435
730
|
end
|
731
|
+
|
732
|
+
# Now stop everything that is still running but which isn't needed.
|
733
|
+
if RunningConfig.has_key?(Coutgoing)
|
734
|
+
(RunningConfig[Coutgoing].keys - new_config[Coutgoing].keys).each do |unneeded_server_key|
|
735
|
+
RunningConfig[Coutgoing][unneeded_server_key].stop_server
|
736
|
+
end
|
737
|
+
end
|
436
738
|
end
|
739
|
+
else
|
740
|
+
# This is where the code goes that sets up traditional proxy destinations.
|
741
|
+
# This is a future TODO item.
|
437
742
|
end
|
438
|
-
|
439
|
-
|
743
|
+
end
|
744
|
+
|
745
|
+
#EventMachine.set_effective_user = config[Cuser] if config[Cuser] and RunningConfig[Cuser] != config[Cuser]
|
746
|
+
run_as(config[Cuser],config[Cgroup]) if (config[Cuser] and RunningConfig[Cuser] != config[Cuser]) or (config[Cgroup] and RunningConfig[Cgroup] != config[Cgroup])
|
747
|
+
new_config[Cuser] = config[Cuser]
|
748
|
+
new_config[Cgroup] = config[Cgroup]
|
749
|
+
|
750
|
+
ProxyBag.server_unavailable_timeout ||= config[Ctimeout]
|
751
|
+
ProxyBag.chunked_encoding_threshold = config[Cchunked_encoding_threshold] || 16384
|
752
|
+
|
753
|
+
unless RunningConfig[:initialized]
|
440
754
|
EventMachine.add_periodic_timer(2) { ProxyBag.expire_clients }
|
441
755
|
EventMachine.add_periodic_timer(1) { ProxyBag.update_ctime }
|
756
|
+
new_config[:initialized] = true
|
442
757
|
end
|
758
|
+
|
759
|
+
RunningConfig.replace new_config
|
760
|
+
end
|
761
|
+
|
762
|
+
|
763
|
+
# This can be used to change the effective user and group that
|
764
|
+
# Swiftiply is running as.
|
765
|
+
|
766
|
+
def self.run_as(user = "nobody", group = "nobody")
|
767
|
+
Process.initgroups(user,Etc.getgrnam(group).gid) if user and group
|
768
|
+
::Process::GID.change_privilege(Etc.getgrnam(group).gid) if group
|
769
|
+
::Process::UID.change_privilege(Etc.getpwnam(user).uid) if user
|
770
|
+
rescue Errno::EPERM
|
771
|
+
raise "Failed to change the effective user to #{user} and the group to #{group}"
|
443
772
|
end
|
444
773
|
end
|
445
774
|
end
|