swiftiply 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|