代码自我审查的一些体会

作者:Lightning@小宝 发布时间:July 11, 2010 分类:Python/Java/Erlang学习

转自:庄周梦蝶
1、首先态度需要端正,做代码的自我审查并不是否定自己,而是给自己将工作做得更好的一次机会。在审查过程中要尽量将自己作为一个旁观者的心态去审查自己的代码,尽管这比较困难。

2、代码审查离不开重构,在审查过程中发现任何坏味道都请使用重构去改善,发现缺乏测试的地方要及时补充测试,不要让BUG遗漏。

3、代码的自我审查可能不是越早越好,隔一段时间之后回去看自己写的东西,对一些设计上的选择能有更客观的评价,在审查的过程中可能需要重新去理解代码,在此过程中可以检查自己代码的可读性,并思考如何改善可读性,切记代码首先是给人读的。

4、审查过程中需要记录下一些犯下的错误,以及当时为什么会犯下这样的错误,建立自己的bug数据库,并时常review,在以后的工作中避免同样的错误。

5、代码的自我审查应该是一个持续性的过程,而非特定时间的特定行动,时常审查自己的代码,不仅能辨析自己的得失,还能够进一步提高自己在未来工作中的设计能力和预见能力。

6、代码的自我审查跟团队成员之间的相互review并不矛盾,在相互review之前做一个自我审查,有助于提高review的效率,包括可读性的提高和一些一般错误的避免。

7、代码自我审查的一些常见注意点:
(0)自认为绝不会出错,并且从来没有审查过的代码。
(1)注意else语句,if条件下的子语句通常可能是个正常的流程,而else意味着异常的情况或者特殊的场景,你可能特别注意怎么处理正常的情况,却忽略了else子句的实现细节,如该释放的锁没释放,该递减的计数没有递减,该赋予特殊值却没有赋予等等。
(2)注意空的方法,没有方法体的方法,是不需要实现?还是忘了实现?
(3)注意switch语句,有没有忘了break?这种错误通过findbugs之类的静态代码检查工具都能避免。
(4)注意大块的注释,为什么这么多注释?是代码写的很糟糕?还是遗留的注释?遗留的注释会误导人,要及时删除。
(5)注意一些看起来“不合常理”的代码,这样的代码很多都是基于所谓性能考虑而优化过的代码,这样的优化是否还需要?是否能去除这些“奇怪”的代码也能实现正常的需求?
(6)对客户端的使用有假设的代码,假设用户只会这么用,假设用户只会用到返回对象中的某些属性,其他属性一定不会用到?不要对客户代码做假设!这个客户代码包括外部用户和调用这个模块的内部代码。
(7)标注了FIXME、TODO之类task标签的代码,是否忘了修复BUG,实现功能?
(8)任何超过15行以上的方法,这些方法是否能拆分成更细粒度的方法,并保持在同一个抽象层次上?
(9)任何在代码中出现的常量值,是否应该提取出来成为单独的常量,常量的默认值设置是否合理?
(10) 任何持有容器的代码,提供了放入容器的方法,是否提供了从容器中移除对象的方法?确保没有内存泄漏的隐患。
(11)重构中提到的其他坏味道,别放过它们,但是也不要追求完美,OO不是圣杯,如果能简单的实现一个算法,你不要引入3个对象和4个接口。
(12)在review最后能列出一张清单,开列下该项目面临的风险点,并提出解决办法,然后按照这张清单去review关键代码,着重检查异常情况下的处理。风险点的review,我建议可以放在后面,在一般性错误解决之后进行,此时你对代码也将再度变的熟悉。

apache开源DB之derby

作者:Lightning@小宝 发布时间:July 3, 2010 分类:Python/Java/Erlang学习

Derby: http://db.apache.org/derby/

最近有个小项目要用一个比较轻量级的存储系统, 已经选择了Bdb的Java版本做开发,但是一直有问题存在;真想用mysql感觉有点重了,完全没有必要。今天用来了一整天对Hsql,Derby,Sqlite都做了些了解,它们都支持标准SQL, Hsql很轻量只要几百KB的Jar包就足够了,Derby的jar包2M多,支持网络与内嵌两种工作模式,在功能上也比较全面就选它了,另一个原因就是公司里的同事在另一个项目中也用到了Derby,哈哈!
Derby是一个开源的数据库,我下面介绍derby作为内嵌式数据库开发入门。

准备工作:先去Derby的官网下载它的Jar包;

第一步创建Derby Database Engine

String driver = "org.apache.derby.jdbc.EmbeddedDriver";
try {
    Class.forName(driver); 
} catch(java.lang.ClassNotFoundException e) {
}

第二步创建我们的数据库

String connectionURL = "jdbc:derby:" + dbName + ";create=true";
try {
    conn = DriverManager.getConnection(connectionURL);
    //do something
}  catch (Throwable e)  {   
}

接下来你只要熟悉JDBC就可以,创建表:

Statement s = conn.createStatement();
s.execute("create table home(id int primary key,data VARCHAR(55))");

非常的简单,读写10W行的测试结果让我对写操作不太满意10W行用了200S,跟Bdb相比差距太大了,待续...

Java并发编程小记【转】

作者:Lightning@小宝 发布时间:June 14, 2010 分类:Python/Java/Erlang学习

 1, 保证线程安全的三种方法 :

a, 不要跨线程访问共享变量

b, 使共享变量是 final类型的

c, 将共享变量的操作加上同步

2, 一开始就将类设计成线程安全的 , 比在后期重新修复它 ,更容易 .

3, 编写多线程程序 , 首先保证它是正确的 , 其次再考虑性能 .

4, 无状态或只读对象永远是线程安全的 .

5, 不要将一个共享变量裸露在多线程环境下 (无同步或不可变性保护 )

6, 多线程环境下的延迟加载需要同步的保护 , 因为延迟加载会造成对象重复实例化

7, 对于 volatile 声明的数值类型变量进行运算 , 往往是不安全的 (volatile 只能保证可见性 , 不能保证原子性 ).

详见 volatile 原理与技巧中 , 脏数据问题讨论 .



8, 当一个线程请求获得它自己占有的锁时 ( 同一把锁的嵌套使用 ), 我们称该锁为可重入锁 .

在 jdk1.5 并发包中 , 提供了可重入锁的 java 实现 -ReentrantLock.

9, 每个共享变量 , 都应该由一个唯一确定的锁保护 .

创建与变量相同数目的 ReentrantLock, 使他们负责每个变量的线程安全 .

10,虽然缩小同步块的范围 , 可以提升系统性能 .

但在保证原子性的情况下 , 不可将原子操作分解成多个 synchronized块 .

11, 在没有同步的情况下 , 编译器与处理器运行时的指令执行顺序可能完全出乎意料 .

原因是 , 编译器或处理器为了优化自身执行效率 , 而对指令进行了的重排序 (reordering).

12, 当一个线程在没有同步的情况下读取变量 , 它可能会得到一个过期值 , 但是至少它可以看到那个

线程在当时设定的一个真实数值 . 而不是凭空而来的值 . 这种安全保证 , 称之为 最低限的安全性 (out-of-thin-air safety)

在开发并发应用程序时 , 有时为了大幅度提高系统的吞吐量与性能 , 会采用这种无保障的做法 .

但是针对 , 数值的运算 , 仍旧是被否决的 .



13, volatile 变量 , 只能保证可见性 , 无法保证原子性 .

14, 某些耗时较长的网络操作或 IO, 确保执行时 , 不要占有锁 .

15, 发布 (publish) 对象 , 指的是使它能够被当前范围之外的代码所使用 .( 引用传递 )

对象逸出 (escape), 指的是一个对象在尚未准备好时将它发布 .

原则 : 为防止逸出 , 对象必须要被完全构造完后 , 才可以被发布 ( 最好的解决方式是采用同步 )

this 关键字引用对象逸出

例子 : 在构造函数中 , 开启线程 , 并将自身对象 this 传入线程 , 造成引用传递 .

而此时 , 构造函数尚未执行完 , 就会发生对象逸出了 .

16, 必要时 , 使用 ThreadLocal变量确保线程封闭性 (封闭线程往往是比较安全的 , 但一定程度上会造成性能损耗 )

封闭对象的例子在实际使用过程中 , 比较常见 , 例如 hibernate openSessionInView机制 , jdbc的 connection机制 .

17, 单一不可变对象往往是线程安全的 (复杂不可变对象需要保证其内部成员变量也是不可变的 )

良好的多线程编程习惯是 : 将所有的域都声明为 final, 除非它们是可变的

18, 保证共享变量的发布是安全的 

a, 通过静态初始化器初始化对象 (jls 12.4.2 叙述 , jvm 会保证静态初始化变量是同步的 )

b, 将对象申明为 volatile 或使用 AtomicReference

c, 保证对象是不可变的

d, 将引用或可变操作都由锁来保护

19, 设计线程安全的类 , 应该包括的基本要素 :

a, 确定哪些是可变共享变量

b, 确定哪些是不可变的变量

c, 指定一个管理并发访问对象状态的策略

20, 将数据封装在对象内部 , 并保证对数据的访问是原子的 .

建议采用 volatile javabean 模型或者构造同步的 getter,setter.

21, 线程限制性使构造线程安全的类变得更容易 , 因为类的状态被限制后 , 分析它的线程安全性时 , 就不必检查完整的程序 .

22, 编写并发程序 , 需要更全的注释 , 更完整的文档说明 .

23, 在需要细分锁的分配时 , 使用 java监视器模式好于使用自身对象的监视器锁 .

前者的灵活性更好 .

Object target = new Object();

// 这里使用外部对象来作为监视器 , 而非 this

synchronized(target) {

// TODO

}

针对 java monitor pattern, 实际上 ReentrantLock的实现更易于并发编程 .

功能上 , 也更强大 .

24, 设计并发程序时 , 在保证伸缩性与性能折中的前提下 , 优先考虑将共享变量委托给线程安全的类 .

由它来控制全局的并发访问 .

25, 使用普通同步容器 (Vector, Hashtable) 的迭代器 , 需要外部锁来保证其原子性 .

原因是 , 普通同步容器产生的迭代器是非线程安全的 .



26, 在并发编程中 , 需要容器支持的时候 , 优先考虑使用 jdk 并发容器 


(ConcurrentHashMap, ConcurrentLinkedQueue, CopyOnWriteArrayList…).

27, ConcurrentHashMap, CopyOnWriteArrayList

并发容器的迭代器 , 以及全范围的 size(), isEmpty() 都表现出弱一致性 .

他们只能标示容器当时的一个数据状态 . 无法完整响应容器之后的变化和修改 .

28, 使用有界队列 , 在队列充满或为空时 , 阻塞所有的读与写操作 . ( 实现生产 – 消费的良好方案 )

BlockQueue 下的实现有 LinkedBlockingQueue 与 ArrayBlockingQueue, 前者为链表 , 可变操作频繁优先考虑 , 后者为数组 , 读取操作频繁优先考虑 .

PriorityBlockingQueue 是一个按优先级顺序排列的阻塞队列 , 它可以对所有置入的元素进行排序 ( 实现 Comparator 接口 )

29, 当一个方法 , 能抛出 InterruptedException, 则意味着 , 这个方法是一个可阻塞的方法 , 如果它被中断 , 将提前结束阻塞状态 .

当你调用一个阻塞方法 , 也就意味着 , 本身也称为了一个阻塞方法 , 因为你必须等待阻塞方法返回 .

如果阻塞方法抛出了中断异常 , 我们需要做的是 , 将其往上层抛 , 除非当前已经是需要捕获异常的层次 .

如果当前方法 , 不能抛出 InterruptedException, 可以使用 Thread.currentThread.interrupt() 方法 , 手动进行中断 .

作者:Ken Wu 原文链接:http://kenwublog.com/java-concurrency-in-practise-note

Ruby的魔幻简洁

作者:Lightning@小宝 发布时间:May 30, 2010 分类:Python/Java/Erlang学习

这周公司组织出游了,坐车时间较长没事干,周五从家走的时候看到了还是07年购买的一本ruby入门书籍,顺手拿上准备在路上看看,之前听大庆说ruby语法很简洁优美甚至有些方面超越python,这次真正的体会到了,就拿目录遍历来说说吧。
查看目录内容的两种遍历方式:
each 方法:

Dir.entries("d:\\rubytest").each {|e| puts e}

foreach 方法:

Dir.foreach("d:\\rubytest") {|e| puts e}

完整实例代码:

class IoTest
    def demo1
        Dir.entries("d:\\rubytest").each { |e| puts e }
    end

    def demo2
        Dir.foreach("d:\\rubytest") { |e| puts e }
    end
end

io = IoTest.new
io.demo1
io.demo2

Python版的:

import os
os.listdir('/home/daxi');//not require '.' & '..'

相比php与java而言代码简洁很多,不过我依然喜欢Python的哲学, 一门语言语法优美精炼与否不是最重要的。

Fastcgi的简易实现[Python版本]备忘录

作者:Lightning@小宝 发布时间:December 10, 2009 分类:Python/Java/Erlang学习

python代码实现简易的fastcgi请求

#!/usr/bin/env python

import os
import sys
import select
import string
import socket
import errno
import cgi
import thread
from cStringIO import StringIO
import struct

# Maximum number of requests that can be handled
FCGI_MAX_REQS = 50
FCGI_MAX_CONNS = 50
FCGI_VERSION_1 = 1
# Can this application multiplex connections?
FCGI_MPXS_CONNS = 0

# Record types
FCGI_BEGIN_REQUEST = 1
FCGI_ABORT_REQUEST = 2
FCGI_END_REQUEST = 3
FCGI_PARAMS = 4
FCGI_STDIN = 5
FCGI_STDOUT = 6
FCGI_STDERR = 7
FCGI_DATA = 8
FCGI_GET_VALUES = 9
FCGI_GET_VALUES_RESULT = 10
FCGI_UNKNOWN_TYPE = 11
FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE

# Types of management records
KNOWN_MANAGEMENT_TYPES = [FCGI_GET_VALUES]

FCGI_NULL_REQUEST_ID = 0

# Masks for flags component of FCGI_BEGIN_REQUEST
FCGI_KEEP_CONN = 1

# Values for role component of FCGI_BEGIN_REQUEST
FCGI_RESPONDER = 1
FCGI_AUTHORIZER = 2
FCGI_FILTER = 3

# Values for protocolStatus component of FCGI_END_REQUEST
FCGI_REQUEST_COMPLETE = 0     # Request completed ok
FCGI_CANT_MPX_CONN = 1        # This app cannot multiplex
FCGI_OVERLOADED = 2           # Too busy
FCGI_UNKNOWN_ROLE = 3         # Role value not known

# Struct format types
FCGI_BeginRequestBody = "!HB5x"
FCGI_Record_header = "!BBHHBx"
FCGI_UnknownTypeBody = "!B7x"
FCGI_EndRequestBody = "!IB3x"

class Record:
    """Class representing FastCGI records"""
    def __init__(self):
        self.version = FCGI_VERSION_1
        self.rec_type = FCGI_UNKNOWN_TYPE
        self.req_id   = FCGI_NULL_REQUEST_ID
        self.content = ""

        # Only in FCGI_BEGIN_REQUEST
        self.role = None
        self.flags = None
        self.keep_conn = 0

        # Only in FCGI_UNKNOWN_TYPE
        self.unknownType = None

        # Only in FCGI_END_REQUEST
        self.appStatus = None
        self.protocolStatus = None

    def read_pair(self, data, pos):
        namelen = struct.unpack("!B", data[pos])[0]
        if namelen & 128:
            # 4-byte name length
            namelen = struct.unpack("!I", data[pos:pos+4])[0] & 0x7fffffff
            pos += 4
        else:
            pos += 1

        valuelen = struct.unpack("!B", data[pos])[0]
        if valuelen & 128:
            # 4-byte value length
            valuelen = struct.unpack("!I", data[pos:pos+4])[0] & 0x7fffffff
            pos += 4
        else:
            pos += 1

        name = data[pos:pos+namelen]
        pos += namelen
        value = data[pos:pos+valuelen]
        pos += valuelen

        return (name, value, pos)

    def write_pair(self, name, value):
        namelen = len(name)
        if namelen < 128:
            data = struct.pack("!B", namelen)
        else:
            # 4-byte name length
            data = struct.pack("!I", namelen | 0x80000000L)

        valuelen = len(value)
        if valuelen < 128:
            data += struct.pack("!B", value)
        else:
            # 4-byte value length
            data += struct.pack("!I", value | 0x80000000L)

        return data + name + value
        
    def readRecord(self, sock):
        data = sock.recv(8)
        if not data:
            # No data recieved. This means EOF. 
            return None
        
        fields = struct.unpack(FCGI_Record_header, data)
        (self.version, self.rec_type, self.req_id,
         contentLength, paddingLength) = fields
        
        self.content = ""
        while len(self.content) < contentLength:
            data = sock.recv(contentLength - len(self.content))
            self.content = self.content + data
        if paddingLength != 0:
            sock.recv(paddingLength)
        
        # Parse the content information
        if self.rec_type == FCGI_BEGIN_REQUEST:
            (self.role, self.flags) = struct.unpack(FCGI_BeginRequestBody,
                                                    self.content)
            self.keep_conn = self.flags & FCGI_KEEP_CONN

        elif self.rec_type == FCGI_UNKNOWN_TYPE:
            self.unknownType = struct.unpack(FCGI_UnknownTypeBody, self.content)

        elif self.rec_type == FCGI_GET_VALUES or self.rec_type == FCGI_PARAMS:
            self.values = {}
            pos = 0
            while pos < len(self.content):
                name, value, pos = self.read_pair(self.content, pos)
                self.values[name] = value
        elif self.rec_type == FCGI_END_REQUEST:
            (self.appStatus,
             self.protocolStatus) = struct.unpack(FCGI_EndRequestBody,
                                                  self.content)

        return 1

    def writeRecord(self, sock):
        content = self.content
        if self.rec_type == FCGI_BEGIN_REQUEST:
            content = struct.pack(FCGI_BeginRequestBody, self.role, self.flags)

        elif self.rec_type == FCGI_UNKNOWN_TYPE:
            content = struct.pack(FCGI_UnknownTypeBody, self.unknownType)

        elif self.rec_type == FCGI_GET_VALUES or self.rec_type == FCGI_PARAMS:
            content = ""
            for i in self.values.keys():
                content = content + self.write_pair(i, self.values[i])

        elif self.rec_type == FCGI_END_REQUEST:
            content = struct.pack(FCGI_EndRequestBody, self.appStatus,
                                  self.protocolStatus)

        # Align to 8-byte boundary
        clen = len(content)
        padlen = ((clen + 7) & 0xfff8) - clen
        
        hdr = struct.pack(FCGI_Record_header, self.version, self.rec_type,
                          self.req_id, clen, padlen)
        
        try:
            sock.sendall(hdr + content + padlen*"\x00")
        except socket.error:
            # Write error, probably broken pipe. Exit thread. 
            thread.exit()


class Request:
    """A request, corresponding to an accept():ed connection and
    a FCGI request. 
    """
    def __init__(self, conn, req_handler):
        self.conn = conn
        self.req_handler = req_handler
        
        self.keep_conn = 0
        self.req_id = None

        # Input
        self.env = {}
        self.env_complete = 0
        self.stdin = StringIO()
        self.stdin_complete = 0
        self.data = StringIO()
        self.data_complete = 0

        # Output
        self.out = StringIO()
        self.err = StringIO()

        self.have_finished = 0

    def run(self):
        while 1:
            if self.conn.fileno() < 1:
                # Connection lost
                return

            select.select([self.conn], [], [])
            rec = Record()
            if rec.readRecord(self.conn):
                self._handle_record(rec)
            else:
                # EOF, connection closed. Break loop, end thread. 
                return
                
    def getFieldStorage(self):
        self.stdin.reset()
        return cgi.FieldStorage(fp=self.stdin, environ=self.env,
                                keep_blank_values=1)

    def _flush(self, stream):
        stream.reset()

        rec = Record()
        rec.rec_type = FCGI_STDOUT
        rec.req_id = self.req_id
        data = stream.read()

        if not data:
            # Writing zero bytes would mean stream termination
            return
        
        while data:
            chunk, data = self.getNextChunk(data)
            rec.content = chunk
            rec.writeRecord(self.conn)
        # Truncate
        stream.reset()
        stream.truncate()

    def flush_out(self):
        self._flush(self.out)

    def flush_err(self):
        self._flush(self.err)

    def finish(self, status=0):
        if self.have_finished:
            return

        self.have_finished = 1

        # stderr
        self.err.reset()
        rec = Record()
        rec.rec_type = FCGI_STDERR
        rec.req_id = self.req_id
        data = self.err.read()
        while data:
            chunk, data = self.getNextChunk(data)
            rec.content = chunk
            rec.writeRecord(self.conn)
        rec.content = ""
        rec.writeRecord(self.conn)      # Terminate stream

        # stdout
        self.out.reset()
        rec = Record()
        rec.rec_type = FCGI_STDOUT
        rec.req_id = self.req_id
        data = self.out.read()
        while data:
            chunk, data = self.getNextChunk(data)
            rec.content = chunk
            rec.writeRecord(self.conn)
        rec.content = ""
        rec.writeRecord(self.conn)      # Terminate stream

        # end request
        rec = Record()
        rec.rec_type = FCGI_END_REQUEST
        rec.req_id = self.req_id
        rec.appStatus = status
        rec.protocolStatus = FCGI_REQUEST_COMPLETE
        rec.writeRecord(self.conn)
        if not self.keep_conn:
            self.conn.close()
            thread.exit()
    
    #
    # Record handlers
    #
    def _handle_record(self, rec):
        """Handle record"""
        if rec.req_id == FCGI_NULL_REQUEST_ID:
            # Management record            
            self._handle_man_record(rec)
        else:
            # Application record
            self._handle_app_record(rec)

    def _handle_man_record(self, rec):
        """Handle management record"""
        rec_type = rec.rec_type
        if rec_type in KNOWN_MANAGEMENT_TYPES:
            self._handle_known_man_types(rec)
        else:
            # It's a management record of an unknown
            # type. Signal the error.
            rec = Record()
            rec.rec_type = FCGI_UNKNOWN_TYPE
            rec.unknownType = rec_type
            rec.writeRecord(self.conn)

    def _handle_known_man_types(self, rec):
        if rec.rec_type == FCGI_GET_VALUES:
            reply_rec = Record()
            reply_rec.rec_type = FCGI_GET_VALUES_RESULT

            params = {'FCGI_MAX_CONNS' : FCGI_MAX_CONNS,
                      'FCGI_MAX_REQS' : FCGI_MAX_REQS,
                      'FCGI_MPXS_CONNS' : FCGI_MPXS_CONNS}

            for name in rec.values.keys():
                if params.has_key(name):
                    # We known this value, include in reply
                    reply_rec.values[name] = params[name]

            rec.writeRecord(self.conn)

    def _handle_app_record(self, rec):
        if rec.rec_type == FCGI_BEGIN_REQUEST:
            # Discrete
            self._handle_begin_request(rec)
            return
        elif rec.req_id != self.req_id:
            #print >> sys.stderr, "Recieved unknown request ID", rec.req_id
            # Ignore requests that aren't active
            return
        if rec.rec_type == FCGI_ABORT_REQUEST:
            # Discrete
            rec.rec_type = FCGI_END_REQUEST
            rec.protocolStatus = FCGI_REQUEST_COMPLETE
            rec.appStatus = 0
            rec.writeRecord(self.conn)
            return
        elif rec.rec_type == FCGI_PARAMS:
            # Stream
            self._handle_params(rec)
        elif rec.rec_type == FCGI_STDIN:
            # Stream
            self._handle_stdin(rec)
        elif rec.rec_type == FCGI_DATA:
            # Stream
            self._handle_data(rec)
        else:
            # Should never happen. 
            #print >> sys.stderr, "Recieved unknown FCGI record type", rec.rec_type
            pass

        if self.env_complete and self.stdin_complete:
            # Call application request handler. 
            # The arguments sent to the request handler is:
            # self: us. 
            # req: The request.
            # env: The request environment
            # form: FieldStorage.
            self.req_handler(self, self.env, self.getFieldStorage())

    def _handle_begin_request(self, rec):
        if rec.role != FCGI_RESPONDER:
            # Unknown role, signal error.
            rec.rec_type = FCGI_END_REQUEST
            rec.appStatus = 0
            rec.protocolStatus = FCGI_UNKNOWN_ROLE
            rec.writeRecord(self.conn)
            return

        self.req_id = rec.req_id
        self.keep_conn = rec.keep_conn
        
    def _handle_params(self, rec):
        if self.env_complete:
            # Should not happen
            #print >> sys.stderr, "Recieved FCGI_PARAMS more than once"
            return
        
        if not rec.content:
            self.env_complete = 1

        # Add all vars to our environment
        self.env.update(rec.values)

    def _handle_stdin(self, rec):
        if self.stdin_complete:
            # Should not happen
            #print >> sys.stderr, "Recieved FCGI_STDIN more than once"
            return
        
        if not rec.content:
            self.stdin_complete = 1

        self.stdin.write(rec.content)

    def _handle_data(self, rec):
        if self.data_complete:
            # Should not happen
            #print >> sys.stderr, "Recieved FCGI_DATA more than once"
            return

        if not rec.content:
            self.data_complete = 1
        
        self.data.write(rec.content)

    def getNextChunk(self, data):
        chunk = data[:8192]
        data = data[8192:]
        return chunk, data


class THFCGI:
    def __init__(self, req_handler, fd=sys.stdin):
        self.req_handler = req_handler
        self.fd = fd
        self._make_socket()

    def run(self):
        """Wait & serve. Calls request handler in new
        thread on every request.
        """
        self.sock.listen(5)
        
        while 1:
            (conn, addr) = self.sock.accept()
            thread.start_new_thread(self.accept_handler, (conn, addr))

    def accept_handler(self, conn, addr):
        self._check_good_addrs(addr)
        req = Request(conn, self.req_handler)
        req.run()

    def _make_socket(self):
        """Create socket and verify FCGI environment"""
        try:
            s = socket.fromfd(self.fd.fileno(), socket.AF_INET,
                              socket.SOCK_STREAM)
            s.getpeername()
        except socket.error, (err, errmsg):
            if err != errno.ENOTCONN: 
                raise "No FastCGI environment"

        self.sock = s
        
    def _check_good_addrs(self, addr):
        # Apaches mod_fastcgi seems not to use FCGI_WEB_SERVER_ADDRS. 
        if os.environ.has_key('FCGI_WEB_SERVER_ADDRS'):
            good_addrs = string.split(os.environ['FCGI_WEB_SERVER_ADDRS'], ',')
            good_addrs = map(string.strip, good_addrs) # Remove whitespace
        else:
            good_addrs = None
        
        # Check if the connection is from a legal address
        if good_addrs != None and addr not in good_addrs:
            raise "Connection from invalid server!"

  1. 页码:
  2. 1
  3. 2
  4. 3
  5. 4
  6. ...
  7. 8
我要报警