[Pkg-privacy-commits] [pt-websocket] 01/10: Imported Upstream version 0.1

Ximin Luo infinity0 at moszumanska.debian.org
Sat Aug 22 11:09:14 UTC 2015


This is an automated email from the git hooks/post-receive script.

infinity0 pushed a commit to branch master
in repository pt-websocket.

commit 1edb6a905a6f1bc0e274330596df2fda497ae5fa
Author: Ximin Luo <infinity0 at pwned.gg>
Date:   Sun Mar 2 13:33:46 2014 +0000

    Imported Upstream version 0.1
---
 .gitignore                           |   2 +
 COPYING                              | 121 ++++++++++
 Makefile                             |  34 +++
 README                               |  30 +++
 doc/websocket-transport.txt          | 219 ++++++++++++++++++
 websocket-client/websocket-client.go | 263 +++++++++++++++++++++
 websocket-server/websocket-server.go | 299 ++++++++++++++++++++++++
 websocket/websocket.go               | 431 +++++++++++++++++++++++++++++++++++
 8 files changed, 1399 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1fee725
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/websocket-client
+/websocket-server
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..0e259d4
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+    HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display,
+     communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+     likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+     subject to the limitations in paragraph 4(a), below;
+  v. rights protecting the extraction, dissemination, use and reuse of data
+     in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+     European Parliament and of the Council of 11 March 1996 on the legal
+     protection of databases, and under any national implementation
+     thereof, including any amended or successor version of such
+     directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+     world based on applicable law or treaty, and any national
+     implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+    surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+    warranties of any kind concerning the Work, express, implied,
+    statutory or otherwise, including without limitation warranties of
+    title, merchantability, fitness for a particular purpose, non
+    infringement, or the absence of latent or other defects, accuracy, or
+    the present or absence of errors, whether or not discoverable, all to
+    the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+    that may apply to the Work or any use thereof, including without
+    limitation any person's Copyright and Related Rights in the Work.
+    Further, Affirmer disclaims responsibility for obtaining any necessary
+    consents, permissions or other rights required for any use of the
+    Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+    party to this document and has no duty or obligation with respect to
+    this CC0 or use of the Work.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..b723c86
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,34 @@
+DESTDIR =
+PREFIX = /usr/local
+BINDIR = $(PREFIX)/bin
+VERSION = 0.1
+
+GOBUILDFLAGS =
+# Alternate flags to use gccgo, allowing cross-compiling for x86 from
+# x86_64, and presumably better optimization. Install this package:
+#   apt-get install gccgo-multilib
+# GOBUILDFLAGS = -compiler gccgo -gccgoflags "-O3 -m32 -static-libgo"
+
+all: websocket-server/websocket-server
+
+websocket-server/websocket-server: websocket-server/*.go websocket/*.go
+	cd websocket-server && go build $(GOBUILDFLAGS)
+
+websocket-client/websocket-client: websocket-client/*.go
+	cd websocket-client && go build $(GOBUILDFLAGS)
+
+doc/websocket-server.1: websocket-server/websocket-server
+	help2man --no-info --name "WebSocket server pluggable transport" --version-string "$(VERSION)" -o "$@" "$<"
+
+install: websocket-server/websocket-server
+	mkdir -p "$(DESTDIR)$(BINDIR)"
+	cp -f websocket-server/websocket-server "$(DESTDIR)$(BINDIR)"
+
+clean:
+	rm -f websocket-server/websocket-server websocket-client/websocket-client
+	rm -f doc/websocket-server.1
+
+fmt:
+	go fmt ./websocket-server ./websocket-client ./websocket
+
+.PHONY: all install clean fmt
diff --git a/README b/README
new file mode 100644
index 0000000..718dd67
--- /dev/null
+++ b/README
@@ -0,0 +1,30 @@
+This is a server transport plugin for the websocket pluggable transport
+for Tor. For information on the protocol, see
+doc/websocket-transport.txt.
+
+To the extent possible under law, the authors have dedicated all
+copyright and related and neighboring rights to this software to the
+public domain worldwide. This software is distributed without any
+warranty. See COPYING.
+
+
+== How to build and install
+
+In order to build, you need development tools for the Go programming
+language ("apt-get install golang" on Debian).
+        $ make
+        # make install
+
+
+== How to run a relay
+
+You will need a version of Tor supporting the extended OR port protocol.
+Git master as of 15 August, 2013 works.
+
+        $ git clone https://git.torproject.org/tor.git
+
+Add configuration like the following to the relay's torrc. You can
+change the --port option; make sure that port is open in the firewall.
+
+        ExtORPort 6669
+        ServerTransportPlugin websocket exec /usr/local/bin/websocket-server --port 9901
diff --git a/doc/websocket-transport.txt b/doc/websocket-transport.txt
new file mode 100644
index 0000000..a9d839f
--- /dev/null
+++ b/doc/websocket-transport.txt
@@ -0,0 +1,219 @@
+Title: WebSocket pluggable transport
+Author: David Fifield
+
+Overview
+
+  This proposal describes the "websocket" pluggable transport for Tor.
+  It uses the WebSocket protocol now implemented by many web browsers.
+  It is mostly a straightforward description of proxying WebSocket to
+  plain TCP, with special consideration for a base64 encoding for agents
+  that don't support binary WebSocket frames.
+
+Motivation
+
+  The WebSocket protocol is used by the "flash proxy" system that uses
+  web browsers as temporary proxies; browsers may connect to a relay
+  that supports this pluggable transport. Additionally, if WebSocket has
+  a lot of non-Tor use, it becomes a good target for tunneling, perhaps
+  in conjunction with a lower layer of obfuscation. WebSocket commonly
+  works over HTTP ports that are likely to get through a firewall.
+
+WebSocket overview
+
+  WebSocket is a protocol (rather, several mostly compatible protocols)
+  aimed at exposing socket-like functionality to JavaScript in web
+  browsers. It is partially aimed at supplanting techniques such as HTTP
+  long polling for client–server communication. WebSocket provides
+  bidirectional communication between a client and server, sufficient to
+  tunnel Tor traffic. A WebSocket session begins with an HTTP Upgrade
+  handshake. The socket carries data broken into variable-length
+  "messages" which are further broken into "frames." There are
+  distinguished frame opcodes that serve to send either data or control
+  information. Frames sent by the client (but not the server) are XORed
+  with a repeating 32-bit mask that is randomly generated per-frame.
+
+  Broadly speaking, there are two versions of WebSocket: the older
+  "hixie" protocol, and the newer "hybi" protocol which is now RFC 6455.
+  There are subprotocols within these two versions that differ only in
+  small ways: "hixie-75" and "hixie-76"; and "hybi-7", "hybi-10", and
+  "hybi-17". The older "hybi" sockets were supported by Firefox 4 and
+  Opera 11, but were later disabled because of problems with interaction
+  with reverse HTTP proxies. Current versions of Firefox and Chrome
+  support "hybi" sockets, while Safari only supports "hixie".
+
+  The "hybi" sockets support text frames and binary frames. Text frames
+  may only include UTF-8–encoded text; it is an error if payload doesn't
+  decode. Binary frames may contain any binary data. However, not all
+  web browsers support binary frames; they were first added to Firefox
+  in version 11. The "hixie" sockets have only text frames.
+
+Method name
+
+  The method name of the transport is "websocket". For example, these
+  are possible torrc configurations for a client and server,
+  respectively:
+
+UseBridges 1
+ClientTransportPlugin websocket exec /usr/libexec/tor-websocket-proxy --client
+Bridge websocket 198.51.100.1
+
+ServerTransportPlugin websocket exec /usr/libexec/tor-websocket-proxy --server
+
+The base64 subprotocol
+
+  The most convenient way to tunnel data over WebSocket is with binary
+  frames, but not all web browsers support binary frames. To work around
+  this, the "base64" subprotocol encodes binary data as base64 within
+  text frames. A client that knows it does not support binary frames
+  requests the base64 subprotocol by including "base64" in the value of
+  the Sec-WebSocket-Protocol header field. A server that also supports
+  this subprotocol by sending the value "base64" (and only "base64") in
+  the Sec-WebSocket-Protocol header field of its response. See under
+  "Examples" for examples of handshakes like this.
+
+  The base64 encoding is applied at the message level, not the frame
+  level. This means, in particular, that any '=' padding occurs only at
+  the end of a message, not at the end of each of its constituent
+  frames. So, for example, the 5-byte message "Hello", whose base64
+  encoding is "SGVsbG8=", may be sent as one text frame as follows:
+
+    0x81 0x08 "SGVsbG8="
+
+  or, for example, as two frames (one of 2 bytes and one of 6 bytes):
+
+    0x01 0x02 "SG" 0x81 0x06 "VsbG8="
+
+  When sent by a client, all frames including these must be masked. Here
+  is an example of a masked base64-encoded message sent as a single
+  frame (using the masking key 0x12345678):
+
+    0x81 0x18 0x12 0x34 0x56 0x78 0x41 0x73 0x00 0x0b 0x70 0x73 0x6e 0x45
+
+Examples
+
+  Here are examples of WebSocket handshakes and the beginning of data
+  transfer. The data is the beginning of a Tor connection (i.e., it
+  begins with a TLS handshake). Data are shown using C string syntax.
+  "> " at the beginning of a line indicates client-to-server
+  communication; "< " is server-to-client. "[...]" indicates contents
+  omitted for brevity. Newlines in the presentation are not significant.
+  This section is non-normative.
+
+  Using "hybi"/RFC 6455 WebSocket with binary frames:
+
+> GET / HTTP/1.1\r\n
+> Host: 192.0.2.1:80\r\n
+> Origin: http://example.com\r\n
+> Sec-WebSocket-Version: 13\r\n
+> Sec-WebSocket-Key: mzo2xSF9N8VUxuefqO0RSw==\r\n
+> Connection: Upgrade\r\n
+> Upgrade: websocket\r\n
+> \r\n
+< HTTP/1.1 101 Switching Protocols\r\n
+< Upgrade: websocket\r\n
+< Connection: Upgrade\r\n
+< Sec-WebSocket-Accept: fM0KjD7ixoxkl4PEXU6tNaTveSg=\r\n
+< \r\n
+> \x82\xfe\x01\x04\xc9\xd6\xdd\x29\xdf\xd5\xde\x29\x36\xd7[...]
+< \x16\x03\x01\x00\x31\x02\x00\x00\x2d\x03[...]
+
+  Using "hybi"/RFC 6455 WebSocket with the base64 subprotocol:
+
+> GET / HTTP/1.1\r\n
+> Host: 192.0.2.1:80\r\n
+> Origin: http://example.com\r\n
+> Sec-WebSocket-Version: 13\r\n
+> Sec-WebSocket-Protocol: base64\r\n
+> Sec-WebSocket-Key: k5Ybhw0XBDeBfmda1J9ooQ==\r\n
+> Connection: Upgrade\r\n
+> Upgrade: websocket\r\n
+> \r\n
+< HTTP/1.1 101 Switching Protocols\r\n
+< Upgrade: websocket\r\n
+< Connection: Upgrade\r\n
+< Sec-WebSocket-Accept: LYWpflPUHdal8U1BLPXWR3iqUrI=\r\n
+< Sec-WebSocket-Protocol: base64\r\n
+< \r\n
+> \x81\xfe\x01\x58\xbd\x94\x2a\x31\xfb\xf3\x67\x75\xfc\xc4[...]
+< \x81\x7e\x04\xd0FgMBADECAA[...]
+
+Considerations specific to pluggable transports
+
+  Endpoints must implement WebSocket according to RFC 6455; for example,
+  a server MUST close the connection if it receives an unmasked frame
+  from a client, and a client MUST close the connection if it receives a
+  masked frame from a server (RFC 6455 section 5.1). There are also
+  additional requirements for WebSocket when used as a Tor pluggable
+  transport.
+
+  Clients MUST implement the RFC 6455 version of the protocol and use it
+  for all connections. Servers MUST implement the RFC 6455 version of
+  the protocol and MAY also implement earlier versions. That is, a
+  server MAY check a client HTTP request to see if it matches an earlier
+  version of the protocol, and MAY begin communicating using that
+  protocol. Section 4.4 of RFC 6455 discusses supporting multiple
+  versions of the protocol.
+
+  Servers MUST support binary frames (opcode 2). Servers MAY also
+  support text frames (opcode 1). Servers supporting text frames MUST
+  implement the base64 subprotocol and accept it when requested by a
+  client in the Sec-WebSocket-Protocol header field. Text frames MUST
+  NOT be sent by either side if the base64 subprotocol has not been
+  negotiated. Any endpoint receiving a text frame when base64 has not
+  been negotiated, or a text message that cannot be decoded as base64,
+  MUST close the connection.
+
+  A client MUST NOT proceed after receiving any HTTP response status
+  code other than 101. In particular, it MUST NOT follow redirections
+  such as 301.
+
+  Endpoints SHOULD respond to Ping frames with a single Pong frame, but
+  nothing in this specification requires the sending of Ping frames.
+
+  Message and frame boundaries are not meaningful. Received non-control
+  messages are concatenated, in order, to reconstruct the original
+  stream. Endpoints SHOULD limit the size of messages they send. All
+  messages SHOULD be sent in a single frame.
+
+  Endpoints MUST limit the size of messages and frames that they will
+  buffer. When the sum of the length of already-buffered data and the
+  length of the next frame exceeds the limit, the endpoint MUST close
+  the connection and SHOULD do so with a status code of 1009 (see RFC
+  6455 section 7.4.1). Endpoints MUST be capable of receiving messages
+  containing up to 16384 bytes of binary data; this may require
+  buffering up to 21848 bytes of UTF-8–encoded base64 text.
+
+Questions/extensions
+
+  WebSocket also has a TLS-wrapped version, identified by using the
+  "wss" (as opposed to "ws") URL scheme. An advantage of this when
+  tunneling through a browser is that the TLS handshake will be exactly
+  that of a browser. However, this probably requires the certificates of
+  relays' server transport plugins to be trusted by browsers.
+
+References
+
+  "Pluggable transports for circumvention"
+  https://gitweb.torproject.org/torspec.git/blob/HEAD:/proposals/180-pluggable-transport.txt
+
+  RFC 6455, "The WebSocket Protocol" (a.k.a. hybi-17)
+  https://tools.ietf.org/html/rfc6455
+
+  "The WebSocket protocol (draft-ietf-hybi-thewebsocketprotocol-10)"
+  (a.k.a. hybi-10)
+  https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10
+
+  "The WebSocket protocol (draft-ietf-hybi-thewebsocketprotocol-7)"
+  (a.k.a. hybi-7)
+  https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-7
+
+  "The WebSocket protocol (draft-ietf-hybi-thewebsocketprotocol-00)"
+  (a.k.a. hybi-00, hixie-76)
+  https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00
+
+  "The Web Socket protocol (draft-hixie-thewebsocketprotocol-75)"
+  (a.k.a. hixie-75)
+  https://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75
+
+  Browser support matrix
+  http://autobahn.ws/testsuite/reports/clients/index.html
diff --git a/websocket-client/websocket-client.go b/websocket-client/websocket-client.go
new file mode 100644
index 0000000..fe38c9d
--- /dev/null
+++ b/websocket-client/websocket-client.go
@@ -0,0 +1,263 @@
+// Tor websocket client transport plugin.
+//
+// Usage in torrc:
+// 	UseBridges 1
+// 	Bridge websocket X.X.X.X:YYYY
+// 	ClientTransportPlugin websocket exec ./websocket-client
+package main
+
+import (
+	"code.google.com/p/go.net/websocket"
+	"flag"
+	"fmt"
+	"io"
+	"net"
+	"net/url"
+	"os"
+	"os/signal"
+	"sync"
+	"syscall"
+	"time"
+)
+
+import "git.torproject.org/pluggable-transports/goptlib.git"
+
+var ptInfo pt.ClientInfo
+
+const ptMethodName = "websocket"
+const bufSiz = 1500
+
+var logFile = os.Stderr
+
+// When a connection handler starts, +1 is written to this channel; when it
+// ends, -1 is written.
+var handlerChan = make(chan int)
+
+var logMutex sync.Mutex
+
+func usage() {
+	fmt.Printf("Usage: %s [OPTIONS]\n", os.Args[0])
+	fmt.Printf("WebSocket client pluggable transport for Tor.\n")
+	fmt.Printf("Works only as a managed proxy.\n")
+	fmt.Printf("\n")
+	fmt.Printf("  -h, --help    show this help.\n")
+	fmt.Printf("  --log FILE    log messages to FILE (default stderr).\n")
+	fmt.Printf("  --socks ADDR  listen for SOCKS on ADDR.\n")
+}
+
+func log(format string, v ...interface{}) {
+	dateStr := time.Now().Format("2006-01-02 15:04:05")
+	logMutex.Lock()
+	defer logMutex.Unlock()
+	msg := fmt.Sprintf(format, v...)
+	fmt.Fprintf(logFile, "%s %s\n", dateStr, msg)
+}
+
+func proxy(local *net.TCPConn, ws *websocket.Conn) {
+	var wg sync.WaitGroup
+
+	wg.Add(2)
+
+	// Local-to-WebSocket read loop.
+	go func() {
+		buf := make([]byte, bufSiz)
+		var err error
+		for {
+			n, er := local.Read(buf[:])
+			if n > 0 {
+				ew := websocket.Message.Send(ws, buf[:n])
+				if ew != nil {
+					err = ew
+					break
+				}
+			}
+			if er != nil {
+				err = er
+				break
+			}
+		}
+		if err != nil && err != io.EOF {
+			log("%s", err)
+		}
+		local.CloseRead()
+		ws.Close()
+
+		wg.Done()
+	}()
+
+	// WebSocket-to-local read loop.
+	go func() {
+		var buf []byte
+		var err error
+		for {
+			er := websocket.Message.Receive(ws, &buf)
+			if er != nil {
+				err = er
+				break
+			}
+			n, ew := local.Write(buf)
+			if ew != nil {
+				err = ew
+				break
+			}
+			if n != len(buf) {
+				err = io.ErrShortWrite
+				break
+			}
+		}
+		if err != nil && err != io.EOF {
+			log("%s", err)
+		}
+		local.CloseWrite()
+		ws.Close()
+
+		wg.Done()
+	}()
+
+	wg.Wait()
+}
+
+func handleConnection(conn *pt.SocksConn) error {
+	defer conn.Close()
+
+	handlerChan <- 1
+	defer func() {
+		handlerChan <- -1
+	}()
+
+	var ws *websocket.Conn
+
+	log("SOCKS request for %s", conn.Req.Target)
+	destAddr, err := net.ResolveTCPAddr("tcp", conn.Req.Target)
+	if err != nil {
+		conn.Reject()
+		return err
+	}
+	wsUrl := url.URL{Scheme: "ws", Host: conn.Req.Target}
+	ws, err = websocket.Dial(wsUrl.String(), "", wsUrl.String())
+	if err != nil {
+		err = conn.Reject()
+		return err
+	}
+	log("WebSocket connection to %s", ws.Config().Location.String())
+	defer ws.Close()
+	err = conn.Grant(destAddr)
+	if err != nil {
+		return err
+	}
+
+	proxy(conn.Conn.(*net.TCPConn), ws)
+
+	return nil
+}
+
+func socksAcceptLoop(ln *pt.SocksListener) error {
+	defer ln.Close()
+	for {
+		socks, err := ln.AcceptSocks()
+		if err != nil {
+			if e, ok := err.(*net.OpError); ok && !e.Temporary() {
+				return err
+			}
+			continue
+		}
+		go func() {
+			err := handleConnection(socks)
+			if err != nil {
+				log("SOCKS from %s: %s", socks.RemoteAddr(), err)
+			}
+		}()
+	}
+	return nil
+}
+
+func startListener(addrStr string) (*pt.SocksListener, error) {
+	ln, err := pt.ListenSocks("tcp", addrStr)
+	if err != nil {
+		return nil, err
+	}
+	go func() {
+		err := socksAcceptLoop(ln)
+		if err != nil {
+			log("accept: %s", err)
+		}
+	}()
+	return ln, nil
+}
+
+func main() {
+	var logFilename string
+	var socksAddrStr string
+	var err error
+
+	flag.Usage = usage
+	flag.StringVar(&logFilename, "log", "", "log file to write to")
+	flag.StringVar(&socksAddrStr, "socks", "127.0.0.1:0", "address on which to listen for SOCKS connections")
+	flag.Parse()
+
+	if logFilename != "" {
+		f, err := os.OpenFile(logFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Can't open log file %q: %s.\n", logFilename, err.Error())
+			os.Exit(1)
+		}
+		logFile = f
+	}
+
+	log("starting")
+	ptInfo, err = pt.ClientSetup([]string{ptMethodName})
+	if err != nil {
+		log("error in setup: %s", err)
+		os.Exit(1)
+	}
+
+	listeners := make([]net.Listener, 0)
+	for _, methodName := range ptInfo.MethodNames {
+		switch methodName {
+		case ptMethodName:
+			ln, err := startListener(socksAddrStr)
+			if err != nil {
+				pt.CmethodError(ptMethodName, err.Error())
+				break
+			}
+			pt.Cmethod(ptMethodName, ln.Version(), ln.Addr())
+			log("listening on %s", ln.Addr().String())
+			listeners = append(listeners, ln)
+		default:
+			pt.CmethodError(methodName, "no such method")
+		}
+	}
+	pt.CmethodsDone()
+
+	var numHandlers int = 0
+	var sig os.Signal
+	sigChan := make(chan os.Signal, 1)
+	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
+
+	// wait for first signal
+	sig = nil
+	for sig == nil {
+		select {
+		case n := <-handlerChan:
+			numHandlers += n
+		case sig = <-sigChan:
+		}
+	}
+	for _, ln := range listeners {
+		ln.Close()
+	}
+
+	if sig == syscall.SIGTERM {
+		return
+	}
+
+	// wait for second signal or no more handlers
+	sig = nil
+	for sig == nil && numHandlers != 0 {
+		select {
+		case n := <-handlerChan:
+			numHandlers += n
+		case sig = <-sigChan:
+		}
+	}
+}
diff --git a/websocket-server/websocket-server.go b/websocket-server/websocket-server.go
new file mode 100644
index 0000000..4120df8
--- /dev/null
+++ b/websocket-server/websocket-server.go
@@ -0,0 +1,299 @@
+// Tor websocket server transport plugin.
+//
+// Usage in torrc:
+// 	ExtORPort 6669
+// 	ServerTransportPlugin websocket exec ./websocket-server --port 9901
+package main
+
+import (
+	"encoding/base64"
+	"errors"
+	"flag"
+	"fmt"
+	"io"
+	"net"
+	"net/http"
+	"os"
+	"os/signal"
+	"sync"
+	"syscall"
+	"time"
+)
+
+import "../websocket"
+
+import "git.torproject.org/pluggable-transports/goptlib.git"
+
+const ptMethodName = "websocket"
+const requestTimeout = 10 * time.Second
+
+// "4/3+1" accounts for possible base64 encoding.
+const maxMessageSize = 64*1024*4/3 + 1
+
+var logFile = os.Stderr
+
+var ptInfo pt.ServerInfo
+
+// When a connection handler starts, +1 is written to this channel; when it
+// ends, -1 is written.
+var handlerChan = make(chan int)
+
+func usage() {
+	fmt.Printf("Usage: %s [OPTIONS]\n", os.Args[0])
+	fmt.Printf("WebSocket server pluggable transport for Tor.\n")
+	fmt.Printf("Works only as a managed proxy.\n")
+	fmt.Printf("\n")
+	fmt.Printf("  -h, --help   show this help.\n")
+	fmt.Printf("  --log FILE   log messages to FILE (default stderr).\n")
+	fmt.Printf("  --port PORT  listen on PORT (overrides Tor's requested port).\n")
+}
+
+var logMutex sync.Mutex
+
+func log(format string, v ...interface{}) {
+	dateStr := time.Now().Format("2006-01-02 15:04:05")
+	logMutex.Lock()
+	defer logMutex.Unlock()
+	msg := fmt.Sprintf(format, v...)
+	fmt.Fprintf(logFile, "%s %s\n", dateStr, msg)
+}
+
+// An abstraction that makes an underlying WebSocket connection look like an
+// io.ReadWriteCloser. It internally takes care of things like base64 encoding
+// and decoding.
+type webSocketConn struct {
+	Ws         *websocket.WebSocket
+	Base64     bool
+	messageBuf []byte
+}
+
+// Implements io.Reader.
+func (conn *webSocketConn) Read(b []byte) (n int, err error) {
+	for len(conn.messageBuf) == 0 {
+		var m websocket.Message
+		m, err = conn.Ws.ReadMessage()
+		if err != nil {
+			return
+		}
+		if m.Opcode == 8 {
+			err = io.EOF
+			return
+		}
+		if conn.Base64 {
+			if m.Opcode != 1 {
+				err = errors.New(fmt.Sprintf("got non-text opcode %d with the base64 subprotocol", m.Opcode))
+				return
+			}
+			conn.messageBuf = make([]byte, base64.StdEncoding.DecodedLen(len(m.Payload)))
+			var num int
+			num, err = base64.StdEncoding.Decode(conn.messageBuf, m.Payload)
+			if err != nil {
+				return
+			}
+			conn.messageBuf = conn.messageBuf[:num]
+		} else {
+			if m.Opcode != 2 {
+				err = errors.New(fmt.Sprintf("got non-binary opcode %d with no subprotocol", m.Opcode))
+				return
+			}
+			conn.messageBuf = m.Payload
+		}
+	}
+
+	n = copy(b, conn.messageBuf)
+	conn.messageBuf = conn.messageBuf[n:]
+
+	return
+}
+
+// Implements io.Writer.
+func (conn *webSocketConn) Write(b []byte) (n int, err error) {
+	if conn.Base64 {
+		buf := make([]byte, base64.StdEncoding.EncodedLen(len(b)))
+		base64.StdEncoding.Encode(buf, b)
+		err = conn.Ws.WriteMessage(1, buf)
+		if err != nil {
+			return
+		}
+		n = len(b)
+	} else {
+		err = conn.Ws.WriteMessage(2, b)
+		n = len(b)
+	}
+	return
+}
+
+// Implements io.Closer.
+func (conn *webSocketConn) Close() error {
+	// Ignore any error in trying to write a Close frame.
+	_ = conn.Ws.WriteFrame(8, nil)
+	return conn.Ws.Conn.Close()
+}
+
+// Create a new webSocketConn.
+func newWebSocketConn(ws *websocket.WebSocket) webSocketConn {
+	var conn webSocketConn
+	conn.Ws = ws
+	conn.Base64 = (ws.Subprotocol == "base64")
+	return conn
+}
+
+// Copy from WebSocket to socket and vice versa.
+func proxy(local *net.TCPConn, conn *webSocketConn) {
+	var wg sync.WaitGroup
+	wg.Add(2)
+
+	go func() {
+		_, err := io.Copy(conn, local)
+		if err != nil {
+			log("error copying ORPort to WebSocket")
+		}
+		local.CloseRead()
+		conn.Close()
+		wg.Done()
+	}()
+	go func() {
+		_, err := io.Copy(local, conn)
+		if err != nil {
+			log("error copying WebSocket to ORPort")
+		}
+		local.CloseWrite()
+		conn.Close()
+		wg.Done()
+	}()
+
+	wg.Wait()
+}
+
+func webSocketHandler(ws *websocket.WebSocket) {
+	// Undo timeouts on HTTP request handling.
+	ws.Conn.SetDeadline(time.Time{})
+	conn := newWebSocketConn(ws)
+	defer conn.Close()
+
+	handlerChan <- 1
+	defer func() {
+		handlerChan <- -1
+	}()
+
+	or, err := pt.DialOr(&ptInfo, ws.Conn.RemoteAddr().String(), ptMethodName)
+	if err != nil {
+		log("Failed to connect to ORPort: " + err.Error())
+		return
+	}
+	defer or.Close()
+
+	proxy(or, &conn)
+}
+
+func startListener(addr *net.TCPAddr) (*net.TCPListener, error) {
+	ln, err := net.ListenTCP("tcp", addr)
+	if err != nil {
+		return nil, err
+	}
+	go func() {
+		defer ln.Close()
+		var config websocket.Config
+		config.Subprotocols = []string{"base64"}
+		config.MaxMessageSize = maxMessageSize
+		s := &http.Server{
+			Handler:     config.Handler(webSocketHandler),
+			ReadTimeout: requestTimeout,
+		}
+		err = s.Serve(ln)
+		if err != nil {
+			log("http.Serve: " + err.Error())
+		}
+	}()
+	return ln, nil
+}
+
+func main() {
+	var logFilename string
+	var port int
+
+	flag.Usage = usage
+	flag.StringVar(&logFilename, "log", "", "log file to write to")
+	flag.IntVar(&port, "port", 0, "port to listen on if unspecified by Tor")
+	flag.Parse()
+
+	if logFilename != "" {
+		f, err := os.OpenFile(logFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Can't open log file %q: %s.\n", logFilename, err.Error())
+			os.Exit(1)
+		}
+		logFile = f
+	}
+
+	log("starting")
+	var err error
+	ptInfo, err = pt.ServerSetup([]string{ptMethodName})
+	if err != nil {
+		log("error in setup: %s", err)
+		os.Exit(1)
+	}
+
+	listeners := make([]*net.TCPListener, 0)
+	for _, bindaddr := range ptInfo.Bindaddrs {
+		// Override tor's requested port (which is 0 if this transport
+		// has not been run before) with the one requested by the --port
+		// option.
+		if port != 0 {
+			bindaddr.Addr.Port = port
+		}
+
+		switch bindaddr.MethodName {
+		case ptMethodName:
+			ln, err := startListener(bindaddr.Addr)
+			if err != nil {
+				pt.SmethodError(bindaddr.MethodName, err.Error())
+				break
+			}
+			pt.Smethod(bindaddr.MethodName, ln.Addr())
+			log("listening on %s", ln.Addr().String())
+			listeners = append(listeners, ln)
+		default:
+			pt.SmethodError(bindaddr.MethodName, "no such method")
+		}
+	}
+	pt.SmethodsDone()
+
+	var numHandlers int = 0
+	var sig os.Signal
+	sigChan := make(chan os.Signal, 1)
+	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
+
+	// wait for first signal
+	sig = nil
+	for sig == nil {
+		select {
+		case n := <-handlerChan:
+			numHandlers += n
+		case sig = <-sigChan:
+		}
+	}
+	log("Got first signal %q with %d running handlers.", sig, numHandlers)
+	for _, ln := range listeners {
+		ln.Close()
+	}
+
+	if sig == syscall.SIGTERM {
+		log("Caught signal %q, exiting.", sig)
+		return
+	}
+
+	// wait for second signal or no more handlers
+	sig = nil
+	for sig == nil && numHandlers != 0 {
+		select {
+		case n := <-handlerChan:
+			numHandlers += n
+			log("%d remaining handlers.", numHandlers)
+		case sig = <-sigChan:
+		}
+	}
+	if sig != nil {
+		log("Got second signal %q with %d running handlers.", sig, numHandlers)
+	}
+}
diff --git a/websocket/websocket.go b/websocket/websocket.go
new file mode 100644
index 0000000..5786fdf
--- /dev/null
+++ b/websocket/websocket.go
@@ -0,0 +1,431 @@
+// WebSocket library. Only the RFC 6455 variety of WebSocket is supported.
+//
+// Reading and writing is strictly per-frame (or per-message). There is no way
+// to partially read a frame. Config.MaxMessageSize affords control of the
+// maximum buffering of messages.
+//
+// The reason for using this custom implementation instead of
+// code.google.com/p/go.net/websocket is that the latter has problems with long
+// messages and does not support server subprotocols.
+//   "Denial of Service Protection in Go HTTP Servers"
+//   https://code.google.com/p/go/issues/detail?id=2093
+//   "go.websocket: Read/Copy fail with long frames"
+//   https://code.google.com/p/go/issues/detail?id=2134
+//   http://golang.org/pkg/net/textproto/#pkg-bugs
+//   "To let callers manage exposure to denial of service attacks, Reader should
+//   allow them to set and reset a limit on the number of bytes read from the
+//   connection."
+//   "websocket.Dial doesn't limit response header length as http.Get does"
+//   https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/2Tge6U8-QYI
+//
+// Example usage:
+//
+// func doSomething(ws *WebSocket) {
+// }
+// var config websocket.Config
+// config.Subprotocols = []string{"base64"}
+// config.MaxMessageSize = 2500
+// http.Handle("/", config.Handler(doSomething))
+// err = http.ListenAndServe(":8080", nil)
+
+package websocket
+
+import (
+	"bufio"
+	"bytes"
+	"crypto/rand"
+	"crypto/sha1"
+	"encoding/base64"
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"io"
+	"net"
+	"net/http"
+	"strings"
+)
+
+// Settings for potential WebSocket connections. Subprotocols is a list of
+// supported subprotocols as in RFC 6455 section 1.9. When answering client
+// requests, the first of the client's requests subprotocols that is also in
+// this list (if any) will be used as the subprotocol for the connection.
+// MaxMessageSize is a limit on buffering messages.
+type Config struct {
+	Subprotocols   []string
+	MaxMessageSize int
+}
+
+// Representation of a WebSocket frame. The Payload is always without masking.
+type Frame struct {
+	Fin     bool
+	Opcode  byte
+	Payload []byte
+}
+
+// Return true iff the frame's opcode says it is a control frame.
+func (frame *Frame) IsControl() bool {
+	return (frame.Opcode & 0x08) != 0
+}
+
+// Representation of a WebSocket message. The Payload is always without masking.
+type Message struct {
+	Opcode  byte
+	Payload []byte
+}
+
+// A WebSocket connection after hijacking from HTTP.
+type WebSocket struct {
+	// Conn and ReadWriter from http.ResponseWriter.Hijack.
+	Conn  net.Conn
+	Bufrw *bufio.ReadWriter
+	// Whether we are a client or a server has implications for masking.
+	IsClient bool
+	// Set from a parent Config.
+	MaxMessageSize int
+	// The single selected subprotocol after negotiation, or "".
+	Subprotocol string
+	// Buffer for message payloads, which may be interrupted by control
+	// messages.
+	messageBuf bytes.Buffer
+}
+
+func applyMask(payload []byte, maskKey [4]byte) {
+	for i := 0; i < len(payload); i++ {
+		payload[i] = payload[i] ^ maskKey[i%4]
+	}
+}
+
+func (ws *WebSocket) maxMessageSize() int {
+	if ws.MaxMessageSize == 0 {
+		return 64000
+	}
+	return ws.MaxMessageSize
+}
+
+// Read a single frame from the WebSocket.
+func (ws *WebSocket) ReadFrame() (frame Frame, err error) {
+	var b byte
+	err = binary.Read(ws.Bufrw, binary.BigEndian, &b)
+	if err != nil {
+		return
+	}
+	frame.Fin = (b & 0x80) != 0
+	frame.Opcode = b & 0x0f
+	err = binary.Read(ws.Bufrw, binary.BigEndian, &b)
+	if err != nil {
+		return
+	}
+	masked := (b & 0x80) != 0
+
+	payloadLen := uint64(b & 0x7f)
+	if payloadLen == 126 {
+		var short uint16
+		err = binary.Read(ws.Bufrw, binary.BigEndian, &short)
+		if err != nil {
+			return
+		}
+		payloadLen = uint64(short)
+	} else if payloadLen == 127 {
+		var long uint64
+		err = binary.Read(ws.Bufrw, binary.BigEndian, &long)
+		if err != nil {
+			return
+		}
+		payloadLen = long
+	}
+	if payloadLen > uint64(ws.maxMessageSize()) {
+		err = errors.New(fmt.Sprintf("frame payload length of %d exceeds maximum of %d", payloadLen, ws.MaxMessageSize))
+		return
+	}
+
+	maskKey := [4]byte{}
+	if masked {
+		if ws.IsClient {
+			err = errors.New("client got masked frame")
+			return
+		}
+		err = binary.Read(ws.Bufrw, binary.BigEndian, &maskKey)
+		if err != nil {
+			return
+		}
+	} else {
+		if !ws.IsClient {
+			err = errors.New("server got unmasked frame")
+			return
+		}
+	}
+
+	frame.Payload = make([]byte, payloadLen)
+	_, err = io.ReadFull(ws.Bufrw, frame.Payload)
+	if err != nil {
+		return
+	}
+	if masked {
+		applyMask(frame.Payload, maskKey)
+	}
+
+	return frame, nil
+}
+
+// Read a single message from the WebSocket. Multiple fragmented frames are
+// combined into a single message before being returned. Non-control messages
+// may be interrupted by control frames. The control frames are returned as
+// individual messages before the message that they interrupt.
+func (ws *WebSocket) ReadMessage() (message Message, err error) {
+	var opcode byte = 0
+	for {
+		var frame Frame
+		frame, err = ws.ReadFrame()
+		if err != nil {
+			return
+		}
+		if frame.IsControl() {
+			if !frame.Fin {
+				err = errors.New("control frame has fin bit unset")
+				return
+			}
+			message.Opcode = frame.Opcode
+			message.Payload = frame.Payload
+			return message, nil
+		}
+
+		if opcode == 0 {
+			if frame.Opcode == 0 {
+				err = errors.New("first frame has opcode 0")
+				return
+			}
+			opcode = frame.Opcode
+		} else {
+			if frame.Opcode != 0 {
+				err = errors.New(fmt.Sprintf("non-first frame has nonzero opcode %d", frame.Opcode))
+				return
+			}
+		}
+		if ws.messageBuf.Len()+len(frame.Payload) > ws.MaxMessageSize {
+			err = errors.New(fmt.Sprintf("message payload length of %d exceeds maximum of %d",
+				ws.messageBuf.Len()+len(frame.Payload), ws.MaxMessageSize))
+			return
+		}
+		ws.messageBuf.Write(frame.Payload)
+		if frame.Fin {
+			break
+		}
+	}
+	message.Opcode = opcode
+	message.Payload = ws.messageBuf.Bytes()
+	ws.messageBuf.Reset()
+
+	return message, nil
+}
+
+// Write a single frame to the WebSocket stream. Destructively masks payload in
+// place if ws.IsClient. Frames are always unfragmented.
+func (ws *WebSocket) WriteFrame(opcode byte, payload []byte) (err error) {
+	if opcode >= 16 {
+		err = errors.New(fmt.Sprintf("opcode %d is >= 16", opcode))
+		return
+	}
+	ws.Bufrw.WriteByte(0x80 | opcode)
+
+	var maskBit byte
+	var maskKey [4]byte
+	if ws.IsClient {
+		_, err = io.ReadFull(rand.Reader, maskKey[:])
+		if err != nil {
+			return
+		}
+		applyMask(payload, maskKey)
+		maskBit = 0x80
+	} else {
+		maskBit = 0x00
+	}
+
+	if len(payload) < 126 {
+		ws.Bufrw.WriteByte(maskBit | byte(len(payload)))
+	} else if len(payload) <= 0xffff {
+		ws.Bufrw.WriteByte(maskBit | 126)
+		binary.Write(ws.Bufrw, binary.BigEndian, uint16(len(payload)))
+	} else {
+		ws.Bufrw.WriteByte(maskBit | 127)
+		binary.Write(ws.Bufrw, binary.BigEndian, uint64(len(payload)))
+	}
+
+	if ws.IsClient {
+		_, err = ws.Bufrw.Write(maskKey[:])
+		if err != nil {
+			return
+		}
+	}
+	_, err = ws.Bufrw.Write(payload)
+	if err != nil {
+		return
+	}
+
+	ws.Bufrw.Flush()
+
+	return
+}
+
+// Write a single message to the WebSocket stream. Destructively masks payload
+// in place if ws.IsClient. Messages are always sent as a single unfragmented
+// frame.
+func (ws *WebSocket) WriteMessage(opcode byte, payload []byte) (err error) {
+	return ws.WriteFrame(opcode, payload)
+}
+
+// Split a string on commas and trim whitespace.
+func commaSplit(s string) []string {
+	var result []string
+	if strings.TrimSpace(s) == "" {
+		return result
+	}
+	for _, e := range strings.Split(s, ",") {
+		result = append(result, strings.TrimSpace(e))
+	}
+	return result
+}
+
+// Returns true iff one of the strings in haystack is needle (case-insensitive).
+func containsCase(haystack []string, needle string) bool {
+	for _, e := range haystack {
+		if strings.ToLower(e) == strings.ToLower(needle) {
+			return true
+		}
+	}
+	return false
+}
+
+// One-step SHA-1 hash of a string.
+func sha1Hash(data string) []byte {
+	h := sha1.New()
+	h.Write([]byte(data))
+	return h.Sum(nil)
+}
+
+func httpError(w http.ResponseWriter, bufrw *bufio.ReadWriter, code int) {
+	w.Header().Set("Connection", "close")
+	bufrw.WriteString(fmt.Sprintf("HTTP/1.0 %d %s\r\n", code, http.StatusText(code)))
+	w.Header().Write(bufrw)
+	bufrw.WriteString("\r\n")
+	bufrw.Flush()
+}
+
+// An implementation of http.Handler with a Config. The ServeHTTP function calls
+// Callback assuming WebSocket HTTP negotiation is successful.
+type HTTPHandler struct {
+	Config   *Config
+	Callback func(*WebSocket)
+}
+
+// Implements the http.Handler interface.
+func (handler *HTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	conn, bufrw, err := w.(http.Hijacker).Hijack()
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	defer conn.Close()
+
+	// See RFC 6455 section 4.2.1 for this sequence of checks.
+
+	// 1. An HTTP/1.1 or higher GET request, including a "Request-URI"...
+	if req.Method != "GET" {
+		httpError(w, bufrw, http.StatusMethodNotAllowed)
+		return
+	}
+	if req.URL.Path != "/" {
+		httpError(w, bufrw, http.StatusNotFound)
+		return
+	}
+	// 2. A |Host| header field containing the server's authority.
+	// We deliberately skip this test.
+	// 3. An |Upgrade| header field containing the value "websocket",
+	// treated as an ASCII case-insensitive value.
+	if !containsCase(commaSplit(req.Header.Get("Upgrade")), "websocket") {
+		httpError(w, bufrw, http.StatusBadRequest)
+		return
+	}
+	// 4. A |Connection| header field that includes the token "Upgrade",
+	// treated as an ASCII case-insensitive value.
+	if !containsCase(commaSplit(req.Header.Get("Connection")), "Upgrade") {
+		httpError(w, bufrw, http.StatusBadRequest)
+		return
+	}
+	// 5. A |Sec-WebSocket-Key| header field with a base64-encoded value
+	// that, when decoded, is 16 bytes in length.
+	websocketKey := req.Header.Get("Sec-WebSocket-Key")
+	key, err := base64.StdEncoding.DecodeString(websocketKey)
+	if err != nil || len(key) != 16 {
+		httpError(w, bufrw, http.StatusBadRequest)
+		return
+	}
+	// 6. A |Sec-WebSocket-Version| header field, with a value of 13.
+	// We also allow 8 from draft-ietf-hybi-thewebsocketprotocol-10.
+	var knownVersions = []string{"8", "13"}
+	websocketVersion := req.Header.Get("Sec-WebSocket-Version")
+	if !containsCase(knownVersions, websocketVersion) {
+		// "If this version does not match a version understood by the
+		// server, the server MUST abort the WebSocket handshake
+		// described in this section and instead send an appropriate
+		// HTTP error code (such as 426 Upgrade Required) and a
+		// |Sec-WebSocket-Version| header field indicating the
+		// version(s) the server is capable of understanding."
+		w.Header().Set("Sec-WebSocket-Version", strings.Join(knownVersions, ", "))
+		httpError(w, bufrw, 426)
+		return
+	}
+	// 7. Optionally, an |Origin| header field.
+	// 8. Optionally, a |Sec-WebSocket-Protocol| header field, with a list of
+	// values indicating which protocols the client would like to speak, ordered
+	// by preference.
+	clientProtocols := commaSplit(req.Header.Get("Sec-WebSocket-Protocol"))
+	// 9. Optionally, a |Sec-WebSocket-Extensions| header field...
+	// 10. Optionally, other header fields...
+
+	var ws WebSocket
+	ws.Conn = conn
+	ws.Bufrw = bufrw
+	ws.IsClient = false
+	ws.MaxMessageSize = handler.Config.MaxMessageSize
+
+	// See RFC 6455 section 4.2.2, item 5 for these steps.
+
+	// 1. A Status-Line with a 101 response code as per RFC 2616.
+	bufrw.WriteString(fmt.Sprintf("HTTP/1.0 %d %s\r\n", http.StatusSwitchingProtocols, http.StatusText(http.StatusSwitchingProtocols)))
+	// 2. An |Upgrade| header field with value "websocket" as per RFC 2616.
+	w.Header().Set("Upgrade", "websocket")
+	// 3. A |Connection| header field with value "Upgrade".
+	w.Header().Set("Connection", "Upgrade")
+	// 4. A |Sec-WebSocket-Accept| header field.  The value of this header
+	// field is constructed by concatenating /key/, defined above in step 4
+	// in Section 4.2.2, with the string
+	// "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", taking the SHA-1 hash of this
+	// concatenated value to obtain a 20-byte value and base64-encoding (see
+	// Section 4 of [RFC4648]) this 20-byte hash.
+	const magicGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+	acceptKey := base64.StdEncoding.EncodeToString(sha1Hash(websocketKey + magicGUID))
+	w.Header().Set("Sec-WebSocket-Accept", acceptKey)
+	// 5.  Optionally, a |Sec-WebSocket-Protocol| header field, with a value
+	// /subprotocol/ as defined in step 4 in Section 4.2.2.
+	for _, clientProto := range clientProtocols {
+		for _, serverProto := range handler.Config.Subprotocols {
+			if clientProto == serverProto {
+				ws.Subprotocol = clientProto
+				w.Header().Set("Sec-WebSocket-Protocol", clientProto)
+				break
+			}
+		}
+	}
+	// 6.  Optionally, a |Sec-WebSocket-Extensions| header field...
+	w.Header().Write(bufrw)
+	bufrw.WriteString("\r\n")
+	bufrw.Flush()
+
+	// Call the WebSocket-specific handler.
+	handler.Callback(&ws)
+}
+
+// Return an http.Handler with the given callback function.
+func (config *Config) Handler(callback func(*WebSocket)) http.Handler {
+	return &HTTPHandler{config, callback}
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/pt-websocket.git



More information about the Pkg-privacy-commits mailing list