ed.net.lb.LBCall.java Source code

Java tutorial

Introduction

Here is the source code for ed.net.lb.LBCall.java

Source

// LBCall.java

/**
*      Copyright (C) 2008 10gen Inc.
*  
*    Licensed under the Apache License, Version 2.0 (the "License");
*    you may not use this file except in compliance with the License.
*    You may obtain a copy of the License at
*  
*       http://www.apache.org/licenses/LICENSE-2.0
*  
*    Unless required by applicable law or agreed to in writing, software
*    distributed under the License is distributed on an "AS IS" BASIS,
*    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*    See the License for the specific language governing permissions and
*    limitations under the License.
*/

package ed.net.lb;

import java.io.*;
import java.net.*;
import java.nio.*;
import java.util.*;
import java.util.concurrent.*;
import java.text.*;

import org.apache.commons.cli.*;

import ed.io.*;
import ed.js.*;
import ed.log.*;
import ed.net.*;
import ed.net.nioclient.*;
import ed.cloud.*;
import ed.net.httpserver.*;
import static ed.net.lb.LB.State;
import static ed.net.lb.Mapping.*;
import static ed.net.nioclient.NIOClient.*;

class LBCall extends Call {

    static final long WAIT_FOR_POOL_TIMEOUT = 10000;

    LBCall(LB lb, HttpRequest req, HttpResponse res) {
        _lb = lb;
        _logger = lb._logger;
        _request = req;
        _response = res;
        reset();

        _lb._lastCalls.add(this);

        _environemnt = _lb._router.getEnvironment(_request);
        if (_environemnt == null)
            _lb._loadMonitor.hitSite("unknown");
        else
            _lb._loadMonitor.hitSite(_environemnt.site);
    }

    void reset() {
        _response.clearHeaders();
        _response.setHeader("X-lb", LB.LBIDENT);
        _state = State.WAITING;
        _line = null;
    }

    void success() {
        done();
        _lb._router.success(_request, _response, _lastWent);
        _lb._loadMonitor.hit(_request, _response);
    }

    public void done() {
        super.done();
        _lb.log(this);
    }

    protected InetSocketAddress where() {
        _lastWent = _lb._router.chooseAddress(_environemnt, _request, _request.elapsed() > WAIT_FOR_POOL_TIMEOUT);
        return _lastWent;
    }

    protected InetSocketAddress lastWent() {
        return _lastWent;
    }

    protected void error(ServerErrorType type, Exception e) {
        _logger.debug(1, "backend error", e);
        _lb._loadMonitor._all.networkEvent();
        _lb._router.error(_request, _response, _lastWent, type, e);

        if (type == ServerErrorType.EOF) {
            // debugging for a problem i don't understand yet
            _logger.error("for EOF debugging.  my state: " + _state);
        }

        if (!_response.isCommitted() && _request.getMethod().equalsIgnoreCase("GET")
                && type != ServerErrorType.WEIRD && (_state == State.WAITING || _state == State.IN_HEADER)
                && ++_numFails <= 3) {
            reset();
            _logger.debug(1, "retrying");
            _lb.add(this);
            return;
        }

        try {
            if (!_response.isCommitted()) {
                _response.setResponseCode(500);
                _response.getJxpWriter().print("backend error : " + e + "\n\n");
            }
            _response.done();
            _state = State.ERROR;
            done();
            _lb._loadMonitor.hit(_request, _response);
        } catch (IOException ioe2) {
            _logger.debug("error sending error mesasge to client", ioe2);
        }
    }

    void backendError(ServerErrorType type, String msg) {
        backendError(type, new IOException(msg));
    }

    void backendError(ServerErrorType type, IOException ioe) {
        ioe.printStackTrace();
        System.out.println("HERE");
        _logger.error("backend error", ioe);
        error(type, ioe);
    }

    protected WhatToDo handleRead(ByteBuffer buf, Connection conn) {

        _logger.debug(3, "handleRead  _state:" + _state);

        if (_state == State.WAITING || _state == State.IN_HEADER) {
            // TODO: should i read this all in at once                
            if (_line == null)
                _line = new StringBuilder();
            while (buf.hasRemaining()) {
                char c = (char) buf.get();
                if (c == '\r')
                    continue;
                if (c == '\n') {
                    if (_line.length() == 0) {
                        _logger.debug(3, "end of header");
                        _state = State.READY_TO_STREAM;
                        break;
                    }

                    String line = _line.toString();

                    if (_state == State.WAITING) {
                        int idx = line.indexOf(" ");
                        if (idx < 0) {
                            backendError(ServerErrorType.INVALID, "invalid first line [" + line + "]");
                            return WhatToDo.ERROR;
                        }
                        line = line.substring(idx + 1).trim();
                        idx = line.indexOf(" ");
                        if (idx < 0)
                            _response.setResponseCode(Integer.parseInt(line));
                        else
                            _response.setStatus(Integer.parseInt(line.substring(0, idx)),
                                    line.substring(idx + 1).trim());

                        _logger.debug(3, "got first line ", line);
                        _state = State.IN_HEADER;
                    } else {
                        int idx = line.indexOf(":");
                        if (idx < 0) {
                            backendError(ServerErrorType.INVALID, "invalid line [ " + line + "]");
                            return WhatToDo.ERROR;
                        }
                        String name = line.substring(0, idx);
                        String value = line.substring(idx + 1).trim();
                        _logger.debug(3, "got header line ", line);

                        if (name.equalsIgnoreCase("Connection")) {
                            _keepalive = !value.toLowerCase().contains("close");
                        } else {
                            _response.addHeader(name, value);
                        }

                    }
                    _line.setLength(0);
                    continue;
                }
                _line.append(c);
            }

            if (_state != State.READY_TO_STREAM)
                return WhatToDo.CONTINUE;

            _logger.debug(3, "starting to stream data");
        }

        if (_state == State.READY_TO_STREAM) {
            MyChunk chunk = new MyChunk(this, conn, _response.getContentLength(), buf);
            _response.sendFile(new MySender(chunk));
            _state = State.STREAMING;
        }

        try {
            _response.done();
        } catch (IOException ioe) {
            _logger.debug(1, "client error", ioe);
            return WhatToDo.CLIENT_ERROR;
        }

        if (isDone() && !_keepalive)
            return WhatToDo.DONE_AND_CLOSE;

        if (_request.isHeadRequest()) {
            assert (_response.isFullySent());
            _lbsuccess(conn);
            return WhatToDo.DONE_AND_CONTINUE;
        }

        return WhatToDo.PAUSE;
    }

    void _lbsuccess(Connection conn) {
        conn.done(!_keepalive);
        _state = State.DONE;
        success();
    }

    protected ByteStream fillInRequest(ByteBuffer buf) {
        buf.put(generateRequestHeader().getBytes());
        if (_request.getPostData() != null)
            return _request.getPostData().getByteStream();
        return null;
    }

    String generateRequestHeader() {
        StringBuilder buf = new StringBuilder(_request.getRawHeader().length() + 200);
        buf.append(_request.getMethod().toUpperCase()).append(" ").append(_request.getURL())
                .append(" HTTP/1.0\r\n");
        buf.append("Connection: keep-alive\r\n");
        buf.append(HttpRequest.REAL_IP_HEADER).append(": ").append(_request.getRemoteIP()).append("\r\n");
        buf.append("X-fromlb: ").append(LB.LBIDENT).append("\r\n");

        for (String n : _request.getHeaderNameKeySet()) {

            if (n.equalsIgnoreCase("Connection") || n.equalsIgnoreCase(HttpRequest.REAL_IP_HEADER))
                continue;

            final String v = _request.getHeader(n);

            buf.append(n).append(": ").append(v).append("\r\n");

        }

        _environemnt.getExtraHeaderString(buf);

        buf.append("\r\n");

        _logger.debug(3, "request\n", buf);

        return buf.toString();
    }

    public String toString() {
        return _request.getFullURL();
    }

    public String getStateString() {
        if (isDone())
            return "DONE";
        return _state.toString();
    }

    final LB _lb;
    final Logger _logger;
    final HttpRequest _request;
    final HttpResponse _response;
    final Environment _environemnt;

    int _numFails = 0;

    private InetSocketAddress _lastWent;

    private boolean _keepalive;
    private State _state;
    private StringBuilder _line;

    // --- internal classes

    class MySender extends JSFile.Sender {
        MySender(MyChunk chunk) {
            super(chunk, chunk._length);
            _chunk = chunk;
        }

        public void cancelled() {
            _chunk.cancelled();
        }

        final MyChunk _chunk;
    }

    class MyChunk extends JSFileChunk {
        MyChunk(LBCall call, Connection conn, long length, ByteBuffer buf) {
            _call = call;
            _conn = conn;
            _length = length;
            _buf = buf;

            _data = new MyBinaryData(_buf);
            _sent += _data.length();
        }

        public JSBinaryData getData() {
            return _data;
        }

        public MyChunk getNext() {
            _logger.debug(4, "want next chunk of data");
            if (_sent == _length) {
                _logger.debug(4, "no more data");
                _call._lbsuccess(_conn);
                return null;
            }

            _conn.doRead(_length > 0);
            _sent += _data.length();
            _last = _data.length();
            _logger.debug(_last == 0 ? 4 : 3, "sent " + _sent + "/" + _length);
            return this;
        }

        void cancelled() {
            _conn.userError("file transfer cancelled");
        }

        long _sent = 0;
        long _last = -1;

        final LBCall _call;
        final Connection _conn;
        final long _length;
        final ByteBuffer _buf;
        final MyBinaryData _data;
    }

    class MyBinaryData extends JSBinaryData {
        MyBinaryData(ByteBuffer buf) {
            _buf = buf;
        }

        public int length() {
            return _buf.remaining();
        }

        public void put(ByteBuffer buf) {
            throw new RuntimeException("not allowed");
        }

        public void write(OutputStream out) {
            throw new RuntimeException("not allowed");
        }

        public ByteBuffer asByteBuffer() {
            return _buf;
        }

        final ByteBuffer _buf;
    }

}