package puppy
/ *
Wrappers around http . Request and http . Response to add helper functions needed by the proxy
* /
import (
"bufio"
"bytes"
"crypto/tls"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"reflect"
"strconv"
"strings"
"time"
"github.com/deckarep/golang-set"
"github.com/gorilla/websocket"
"golang.org/x/net/proxy"
)
const (
ToServer = iota
ToClient
)
// A dialer used to create a net.Conn from a network and address
type NetDialer func ( network , addr string ) ( net . Conn , error )
// ProxyResponse is an http.Response with additional fields for use within the proxy
type ProxyResponse struct {
http . Response
bodyBytes [ ] byte
// Id used to reference this response in its associated MessageStorage. Blank string means it is not saved in any MessageStorage
DbId string
// If this response was modified by the proxy, Unmangled is the response before it was modified. If the response was not modified, Unmangled is nil.
Unmangled * ProxyResponse
}
// ProxyRequest is an http.Request with additional fields for use within the proxy
type ProxyRequest struct {
http . Request
// Host where this request is intended to be sent when submitted
DestHost string
// Port that should be used when this request is submitted
DestPort int
// Whether TLS should be used when this request is submitted
DestUseTLS bool
// Response received from the server when this request was submitted. If the request does not have any associated response, ServerResponse is nil.
ServerResponse * ProxyResponse
// If the request was the handshake for a websocket session, WSMessages will be a slice of all the messages that were sent over that session.
WSMessages [ ] * ProxyWSMessage
// If the request was modified by the proxy, Unmangled will point to the unmodified version of the request. Otherwise it is nil.
Unmangled * ProxyRequest
// ID used to reference to this request in its associated MessageStorage
DbId string
// The time at which this request was submitted
StartDatetime time . Time
// The time at which the response to this request was received
EndDatetime time . Time
bodyBytes [ ] byte
tags mapset . Set
// The dialer that should be used when this request is submitted
NetDial NetDialer
}
// WSSession is an extension of websocket.Conn to contain a reference to the ProxyRequest used for the websocket handshake
type WSSession struct {
websocket . Conn
// Request used for handshake
Request * ProxyRequest
}
// ProxyWSMessage represents one message in a websocket session
type ProxyWSMessage struct {
// The type of websocket message
Type int
// The contents of the message
Message [ ] byte
// The direction of the message. Either ToServer or ToClient
Direction int
// If the message was modified by the proxy, points to the original unmodified message
Unmangled * ProxyWSMessage
// The time at which the message was sent (if sent to the server) or received (if received from the server)
Timestamp time . Time
// The request used for the handhsake for the session that this session was used for
Request * ProxyRequest
// ID used to reference to this message in its associated MessageStorage
DbId string
}
// PerformConnect submits a CONNECT request for the given host and port over the given connection
func PerformConnect ( conn net . Conn , destHost string , destPort int ) error {
connStr := [ ] byte ( fmt . Sprintf ( "CONNECT %s:%d HTTP/1.1\r\nHost: %s\r\nProxy-Connection: Keep-Alive\r\n\r\n" , destHost , destPort , destHost ) )
conn . Write ( connStr )
rsp , err := http . ReadResponse ( bufio . NewReader ( conn ) , nil )
if err != nil {
return fmt . Errorf ( "error performing CONNECT handshake: %s" , err . Error ( ) )
}
if rsp . StatusCode != 200 {
return fmt . Errorf ( "error performing CONNECT handshake" )
}
return nil
}
// NewProxyRequest creates a new proxy request with the given destination
func NewProxyRequest ( r * http . Request , destHost string , destPort int , destUseTLS bool ) * ProxyRequest {
var retReq * ProxyRequest
if r != nil {
// Write/reread the request to make sure we get all the extra headers Go adds into req.Header
buf := bytes . NewBuffer ( make ( [ ] byte , 0 ) )
r . Write ( buf )
httpReq2 , err := http . ReadRequest ( bufio . NewReader ( buf ) )
if err != nil {
panic ( err )
}
retReq = & ProxyRequest {
* httpReq2 ,
destHost ,
destPort ,
destUseTLS ,
nil ,
make ( [ ] * ProxyWSMessage , 0 ) ,
nil ,
"" ,
time . Unix ( 0 , 0 ) ,
time . Unix ( 0 , 0 ) ,
make ( [ ] byte , 0 ) ,
mapset . NewSet ( ) ,
nil ,
}
} else {
newReq , _ := http . NewRequest ( "GET" , "/" , nil ) // Ignore error since this should be run the same every time and shouldn't error
newReq . Header . Set ( "User-Agent" , "Puppy-Proxy/1.0" )
newReq . Host = destHost
retReq = & ProxyRequest {
* newReq ,
destHost ,
destPort ,
destUseTLS ,
nil ,
make ( [ ] * ProxyWSMessage , 0 ) ,
nil ,
"" ,
time . Unix ( 0 , 0 ) ,
time . Unix ( 0 , 0 ) ,
make ( [ ] byte , 0 ) ,
mapset . NewSet ( ) ,
nil ,
}
}
// Load the body
bodyBuf , _ := ioutil . ReadAll ( retReq . Body )
retReq . SetBodyBytes ( bodyBuf )
return retReq
}
// ProxyRequestFromBytes parses a slice of bytes containing a well-formed HTTP request into a ProxyRequest. Does NOT correct incorrect Content-Length headers
func ProxyRequestFromBytes ( b [ ] byte , destHost string , destPort int , destUseTLS bool ) ( * ProxyRequest , error ) {
buf := bytes . NewBuffer ( b )
httpReq , err := http . ReadRequest ( bufio . NewReader ( buf ) )
if err != nil {
return nil , err
}
return NewProxyRequest ( httpReq , destHost , destPort , destUseTLS ) , nil
}
// NewProxyResponse creates a new ProxyResponse given an http.Response
func NewProxyResponse ( r * http . Response ) * ProxyResponse {
// Write/reread the request to make sure we get all the extra headers Go adds into req.Header
oldClose := r . Close
r . Close = false
buf := bytes . NewBuffer ( make ( [ ] byte , 0 ) )
r . Write ( buf )
r . Close = oldClose
httpRsp2 , err := http . ReadResponse ( bufio . NewReader ( buf ) , nil )
if err != nil {
panic ( err )
}
httpRsp2 . Close = false
retRsp := & ProxyResponse {
* httpRsp2 ,
make ( [ ] byte , 0 ) ,
"" ,
nil ,
}
bodyBuf , _ := ioutil . ReadAll ( retRsp . Body )
retRsp . SetBodyBytes ( bodyBuf )
return retRsp
}
// NewProxyResponse parses a ProxyResponse from a slice of bytes containing a well-formed HTTP response. Does NOT correct incorrect Content-Length headers
func ProxyResponseFromBytes ( b [ ] byte ) ( * ProxyResponse , error ) {
buf := bytes . NewBuffer ( b )
httpRsp , err := http . ReadResponse ( bufio . NewReader ( buf ) , nil )
if err != nil {
return nil , err
}
return NewProxyResponse ( httpRsp ) , nil
}
// NewProxyWSMessage creates a new WSMessage given a type, message, and direction
func NewProxyWSMessage ( mtype int , message [ ] byte , direction int ) ( * ProxyWSMessage , error ) {
return & ProxyWSMessage {
Type : mtype ,
Message : message ,
Direction : direction ,
Unmangled : nil ,
Timestamp : time . Unix ( 0 , 0 ) ,
DbId : "" ,
} , nil
}
// DestScheme returns the scheme used by the request (ws, wss, http, or https)
func ( req * ProxyRequest ) DestScheme ( ) string {
if req . IsWSUpgrade ( ) {
if req . DestUseTLS {
return "wss"
} else {
return "ws"
}
} else {
if req . DestUseTLS {
return "https"
} else {
return "http"
}
}
}
// FullURL is the same as req.URL but guarantees it will include the scheme, host, and port if necessary
func ( req * ProxyRequest ) FullURL ( ) * url . URL {
var u url . URL
u = * ( req . URL ) // Copy the original req.URL
u . Host = req . Host
u . Scheme = req . DestScheme ( )
return & u
}
// Same as req.FullURL() but uses DestHost and DestPort for the host and port of the URL
func ( req * ProxyRequest ) DestURL ( ) * url . URL {
var u url . URL
u = * ( req . URL ) // Copy the original req.URL
u . Scheme = req . DestScheme ( )
if req . DestUseTLS && req . DestPort == 443 ||
! req . DestUseTLS && req . DestPort == 80 {
u . Host = req . DestHost
} else {
u . Host = fmt . Sprintf ( "%s:%d" , req . DestHost , req . DestPort )
}
return & u
}
// Submit submits the request over the given connection. Does not take into account DestHost, DestPort, or DestUseTLS
func ( req * ProxyRequest ) Submit ( conn net . Conn ) error {
return req . submit ( conn , false , nil )
}
// Submit submits the request in proxy form over the given connection for use with an upstream HTTP proxy. Does not take into account DestHost, DestPort, or DestUseTLS
func ( req * ProxyRequest ) SubmitProxy ( conn net . Conn , creds * ProxyCredentials ) error {
return req . submit ( conn , true , creds )
}
func ( req * ProxyRequest ) submit ( conn net . Conn , forProxy bool , proxyCreds * ProxyCredentials ) error {
// Write the request to the connection
req . StartDatetime = time . Now ( )
if forProxy {
if req . DestUseTLS {
req . URL . Scheme = "https"
} else {
req . URL . Scheme = "http"
}
req . URL . Opaque = ""
if err := req . RepeatableProxyWrite ( conn , proxyCreds ) ; err != nil {
return err
}
} else {
if err := req . RepeatableWrite ( conn ) ; err != nil {
return err
}
}
// Read a response from the server
httpRsp , err := http . ReadResponse ( bufio . NewReader ( conn ) , nil )
if err != nil {
return fmt . Errorf ( "error reading response: %s" , err . Error ( ) )
}
req . EndDatetime = time . Now ( )
prsp := NewProxyResponse ( httpRsp )
req . ServerResponse = prsp
return nil
}
// WSDial performs a websocket handshake over the given connection. Does not take into account DestHost, DestPort, or DestUseTLS
func ( req * ProxyRequest ) WSDial ( conn net . Conn ) ( * WSSession , error ) {
if ! req . IsWSUpgrade ( ) {
return nil , fmt . Errorf ( "could not start websocket session: request is not a websocket handshake request" )
}
upgradeHeaders := make ( http . Header )
for k , v := range req . Header {
for _ , vv := range v {
if ! ( k == "Upgrade" ||
k == "Connection" ||
k == "Sec-Websocket-Key" ||
k == "Sec-Websocket-Version" ||
k == "Sec-Websocket-Extensions" ||
k == "Sec-Websocket-Protocol" ) {
upgradeHeaders . Add ( k , vv )
}
}
}
dialer := & websocket . Dialer { }
dialer . NetDial = func ( network , address string ) ( net . Conn , error ) {
return conn , nil
}
wsconn , rsp , err := dialer . Dial ( req . DestURL ( ) . String ( ) , upgradeHeaders )
if err != nil {
return nil , fmt . Errorf ( "could not dial WebSocket server: %s" , err )
}
req . ServerResponse = NewProxyResponse ( rsp )
wsession := & WSSession {
* wsconn ,
req ,
}
return wsession , nil
}
// WSDial dials the target server and performs a websocket handshake over the new connection. Uses destination information from the request.
func WSDial ( req * ProxyRequest ) ( * WSSession , error ) {
return wsDial ( req , false , "" , 0 , nil , false )
}
// WSDialProxy dials the HTTP proxy server, performs a CONNECT handshake to connect to the remote server, then performs a websocket handshake over the new connection. Uses destination information from the request.
func WSDialProxy ( req * ProxyRequest , proxyHost string , proxyPort int , creds * ProxyCredentials ) ( * WSSession , error ) {
return wsDial ( req , true , proxyHost , proxyPort , creds , false )
}
// WSDialSOCKSProxy connects to the target host through the SOCKS proxy and performs a websocket handshake over the new connection. Uses destination information from the request.
func WSDialSOCKSProxy ( req * ProxyRequest , proxyHost string , proxyPort int , creds * ProxyCredentials ) ( * WSSession , error ) {
return wsDial ( req , true , proxyHost , proxyPort , creds , true )
}
func wsDial ( req * ProxyRequest , useProxy bool , proxyHost string , proxyPort int , proxyCreds * ProxyCredentials , proxyIsSOCKS bool ) ( * WSSession , error ) {
var conn net . Conn
var dialer NetDialer
var err error
if req . NetDial != nil {
dialer = req . NetDial
} else {
dialer = net . Dial
}
if useProxy {
if proxyIsSOCKS {
var socksCreds * proxy . Auth
if proxyCreds != nil {
socksCreds = & proxy . Auth {
User : proxyCreds . Username ,
Password : proxyCreds . Password ,
}
}
socksDialer , err := proxy . SOCKS5 ( "tcp" , fmt . Sprintf ( "%s:%d" , proxyHost , proxyPort ) , socksCreds , proxy . Direct )
if err != nil {
return nil , fmt . Errorf ( "error creating SOCKS dialer: %s" , err . Error ( ) )
}
conn , err = socksDialer . Dial ( "tcp" , fmt . Sprintf ( "%s:%d" , req . DestHost , req . DestPort ) )
if err != nil {
return nil , fmt . Errorf ( "error dialing host: %s" , err . Error ( ) )
}
defer conn . Close ( )
} else {
conn , err = dialer ( "tcp" , fmt . Sprintf ( "%s:%d" , proxyHost , proxyPort ) )
if err != nil {
return nil , fmt . Errorf ( "error dialing proxy: %s" , err . Error ( ) )
}
// always perform a CONNECT for websocket regardless of SSL
if err := PerformConnect ( conn , req . DestHost , req . DestPort ) ; err != nil {
return nil , err
}
}
} else {
conn , err = dialer ( "tcp" , fmt . Sprintf ( "%s:%d" , req . DestHost , req . DestPort ) )
if err != nil {
return nil , fmt . Errorf ( "error dialing host: %s" , err . Error ( ) )
}
}
if req . DestUseTLS {
tls_conn := tls . Client ( conn , & tls . Config {
InsecureSkipVerify : true ,
ServerName : req . DestHost ,
} )
conn = tls_conn
}
return req . WSDial ( conn )
}
// IsWSUpgrade returns whether the request is used to initiate a websocket handshake
func ( req * ProxyRequest ) IsWSUpgrade ( ) bool {
for k , v := range req . Header {
for _ , vv := range v {
if strings . ToLower ( k ) == "upgrade" && strings . Contains ( vv , "websocket" ) {
return true
}
}
}
return false
}
// StripProxyHeaders removes headers associated with requests made to a proxy from the request
func ( req * ProxyRequest ) StripProxyHeaders ( ) {
if ! req . IsWSUpgrade ( ) {
req . Header . Del ( "Connection" )
}
req . Header . Del ( "Accept-Encoding" )
req . Header . Del ( "Proxy-Connection" )
req . Header . Del ( "Proxy-Authenticate" )
req . Header . Del ( "Proxy-Authorization" )
}
// Eq checks whether the request is the same as another request and has the same destination information
func ( req * ProxyRequest ) Eq ( other * ProxyRequest ) bool {
if req . StatusLine ( ) != other . StatusLine ( ) ||
! reflect . DeepEqual ( req . Header , other . Header ) ||
bytes . Compare ( req . BodyBytes ( ) , other . BodyBytes ( ) ) != 0 ||
req . DestHost != other . DestHost ||
req . DestPort != other . DestPort ||
req . DestUseTLS != other . DestUseTLS {
return false
}
return true
}
// Clone returns a request with the same contents and destination information as the original
func ( req * ProxyRequest ) Clone ( ) * ProxyRequest {
buf := bytes . NewBuffer ( make ( [ ] byte , 0 ) )
req . RepeatableWrite ( buf )
newReq , err := ProxyRequestFromBytes ( buf . Bytes ( ) , req . DestHost , req . DestPort , req . DestUseTLS )
if err != nil {
panic ( err )
}
newReq . DestHost = req . DestHost
newReq . DestPort = req . DestPort
newReq . DestUseTLS = req . DestUseTLS
newReq . Header = copyHeader ( req . Header )
return newReq
}
// DeepClone returns a request with the same contents, destination, and storage information information as the original along with a deep clone of the associated response, the unmangled version of the request, and any websocket messages
func ( req * ProxyRequest ) DeepClone ( ) * ProxyRequest {
// Returns a request with the same request, response, and associated websocket messages
newReq := req . Clone ( )
newReq . DbId = req . DbId
if req . Unmangled != nil {
newReq . Unmangled = req . Unmangled . DeepClone ( )
}
if req . ServerResponse != nil {
newReq . ServerResponse = req . ServerResponse . DeepClone ( )
}
for _ , wsm := range req . WSMessages {
newReq . WSMessages = append ( newReq . WSMessages , wsm . DeepClone ( ) )
}
return newReq
}
func ( req * ProxyRequest ) resetBodyReader ( ) {
// yes I know this method isn't the most efficient, I'll fix it if it causes problems later
req . Body = ioutil . NopCloser ( bytes . NewBuffer ( req . BodyBytes ( ) ) )
}
// RepeatableWrite is the same as http.Request.Write except that it can be safely called multiple times
func ( req * ProxyRequest ) RepeatableWrite ( w io . Writer ) error {
defer req . resetBodyReader ( )
return req . Write ( w )
}
// RepeatableWrite is the same as http.Request.ProxyWrite except that it can be safely called multiple times
func ( req * ProxyRequest ) RepeatableProxyWrite ( w io . Writer , proxyCreds * ProxyCredentials ) error {
defer req . resetBodyReader ( )
if proxyCreds != nil {
authHeader := proxyCreds . SerializeHeader ( )
req . Header . Set ( "Proxy-Authorization" , authHeader )
defer func ( ) { req . Header . Del ( "Proxy-Authorization" ) } ( )
}
return req . WriteProxy ( w )
}
// BodyBytes returns the bytes of the request body
func ( req * ProxyRequest ) BodyBytes ( ) [ ] byte {
return DuplicateBytes ( req . bodyBytes )
}
// SetBodyBytes sets the bytes of the request body and updates the Content-Length header
func ( req * ProxyRequest ) SetBodyBytes ( bs [ ] byte ) {
req . bodyBytes = bs
req . resetBodyReader ( )
// Parse the form if we can, ignore errors
req . ParseMultipartForm ( 1024 * 1024 * 1024 ) // 1GB for no good reason
req . ParseForm ( )
req . resetBodyReader ( )
req . Header . Set ( "Content-Length" , strconv . Itoa ( len ( bs ) ) )
}
// FullMessage returns a slice of bytes containing the full HTTP message for the request
func ( req * ProxyRequest ) FullMessage ( ) [ ] byte {
buf := bytes . NewBuffer ( make ( [ ] byte , 0 ) )
req . RepeatableWrite ( buf )
return buf . Bytes ( )
}
// PostParameters attempts to parse POST parameters from the body of the request
func ( req * ProxyRequest ) PostParameters ( ) ( url . Values , error ) {
vals , err := url . ParseQuery ( string ( req . BodyBytes ( ) ) )
if err != nil {
return nil , err
}
return vals , nil
}
// SetPostParameter sets the value of a post parameter in the message body. If the body does not contain well-formed data, it is deleted replaced with a well-formed body containing only the new parameter
func ( req * ProxyRequest ) SetPostParameter ( key string , value string ) {
req . PostForm . Set ( key , value )
req . SetBodyBytes ( [ ] byte ( req . PostForm . Encode ( ) ) )
}
// AddPostParameter adds a post parameter to the body of the request even if a duplicate exists. If the body does not contain well-formed data, it is deleted replaced with a well-formed body containing only the new parameter
func ( req * ProxyRequest ) AddPostParameter ( key string , value string ) {
req . PostForm . Add ( key , value )
req . SetBodyBytes ( [ ] byte ( req . PostForm . Encode ( ) ) )
}
// DeletePostParameter removes a parameter from the body of the request. If the body does not contain well-formed data, it is deleted replaced with a well-formed body containing only the new parameter
func ( req * ProxyRequest ) DeletePostParameter ( key string ) {
req . PostForm . Del ( key )
req . SetBodyBytes ( [ ] byte ( req . PostForm . Encode ( ) ) )
}
// SetURLParameter sets the value of a URL parameter and updates ProxyRequest.URL
func ( req * ProxyRequest ) SetURLParameter ( key string , value string ) {
q := req . URL . Query ( )
q . Set ( key , value )
req . URL . RawQuery = q . Encode ( )
req . ParseForm ( )
}
// URLParameters returns the values of the request's URL parameters
func ( req * ProxyRequest ) URLParameters ( ) url . Values {
vals := req . URL . Query ( )
return vals
}
// AddURLParameter adds a URL parameter to the request ignoring duplicates
func ( req * ProxyRequest ) AddURLParameter ( key string , value string ) {
q := req . URL . Query ( )
q . Add ( key , value )
req . URL . RawQuery = q . Encode ( )
req . ParseForm ( )
}
// DeleteURLParameter removes a URL parameter from the request
func ( req * ProxyRequest ) DeleteURLParameter ( key string ) {
q := req . URL . Query ( )
q . Del ( key )
req . URL . RawQuery = q . Encode ( )
req . ParseForm ( )
}
// AddTag adds a tag to the request
func ( req * ProxyRequest ) AddTag ( tag string ) {
req . tags . Add ( tag )
}
// CheckTag returns whether the request has a given tag
func ( req * ProxyRequest ) CheckTag ( tag string ) bool {
return req . tags . Contains ( tag )
}
// RemoveTag removes a tag from the request
func ( req * ProxyRequest ) RemoveTag ( tag string ) {
req . tags . Remove ( tag )
}
// ClearTag removes all of the tags associated with the request
func ( req * ProxyRequest ) ClearTags ( ) {
req . tags . Clear ( )
}
// Tags returns a slice containing all of the tags associated with the request
func ( req * ProxyRequest ) Tags ( ) [ ] string {
items := req . tags . ToSlice ( )
retslice := make ( [ ] string , 0 )
for _ , item := range items {
str , ok := item . ( string )
if ok {
retslice = append ( retslice , str )
}
}
return retslice
}
// HTTPPath returns the path of the associated with the request
func ( req * ProxyRequest ) HTTPPath ( ) string {
// The path used in the http request
u := * req . URL
u . Scheme = ""
u . Host = ""
u . Opaque = ""
u . User = nil
return u . String ( )
}
// StatusLine returns the status line associated with the request
func ( req * ProxyRequest ) StatusLine ( ) string {
return fmt . Sprintf ( "%s %s %s" , req . Method , req . HTTPPath ( ) , req . Proto )
}
// HeaderSection returns the header section of the request without the additional \r\n at the end
func ( req * ProxyRequest ) HeaderSection ( ) string {
retStr := req . StatusLine ( )
retStr += "\r\n"
for k , vs := range req . Header {
for _ , v := range vs {
retStr += fmt . Sprintf ( "%s: %s\r\n" , k , v )
}
}
return retStr
}
func ( rsp * ProxyResponse ) resetBodyReader ( ) {
// yes I know this method isn't the most efficient, I'll fix it if it causes problems later
rsp . Body = ioutil . NopCloser ( bytes . NewBuffer ( rsp . BodyBytes ( ) ) )
}
// RepeatableWrite is the same as http.Response.Write except that it can safely be called multiple times
func ( rsp * ProxyResponse ) RepeatableWrite ( w io . Writer ) error {
defer rsp . resetBodyReader ( )
return rsp . Write ( w )
}
// BodyBytes returns the bytes contained in the body of the response
func ( rsp * ProxyResponse ) BodyBytes ( ) [ ] byte {
return DuplicateBytes ( rsp . bodyBytes )
}
// SetBodyBytes sets the bytes in the body of the response and updates the Content-Length header
func ( rsp * ProxyResponse ) SetBodyBytes ( bs [ ] byte ) {
rsp . bodyBytes = bs
rsp . resetBodyReader ( )
rsp . Header . Set ( "Content-Length" , strconv . Itoa ( len ( bs ) ) )
}
// Clone returns a response with the same status line, headers, and body as the response
func ( rsp * ProxyResponse ) Clone ( ) * ProxyResponse {
buf := bytes . NewBuffer ( make ( [ ] byte , 0 ) )
rsp . RepeatableWrite ( buf )
newRsp , err := ProxyResponseFromBytes ( buf . Bytes ( ) )
if err != nil {
panic ( err )
}
return newRsp
}
// DeepClone returns a response with the same status line, headers, and body as the original response along with a deep clone of its unmangled version if it exists
func ( rsp * ProxyResponse ) DeepClone ( ) * ProxyResponse {
newRsp := rsp . Clone ( )
newRsp . DbId = rsp . DbId
if rsp . Unmangled != nil {
newRsp . Unmangled = rsp . Unmangled . DeepClone ( )
}
return newRsp
}
// Eq returns whether the response has the same contents as another response
func ( rsp * ProxyResponse ) Eq ( other * ProxyResponse ) bool {
if rsp . StatusLine ( ) != other . StatusLine ( ) ||
! reflect . DeepEqual ( rsp . Header , other . Header ) ||
bytes . Compare ( rsp . BodyBytes ( ) , other . BodyBytes ( ) ) != 0 {
return false
}
return true
}
// FullMessage returns the full HTTP message of the response
func ( rsp * ProxyResponse ) FullMessage ( ) [ ] byte {
buf := bytes . NewBuffer ( make ( [ ] byte , 0 ) )
rsp . RepeatableWrite ( buf )
return buf . Bytes ( )
}
// Returns the status text to be used in the http request
func ( rsp * ProxyResponse ) HTTPStatus ( ) string {
// The status text to be used in the http request. Relies on being the same implementation as http.Response
text := rsp . Status
if text == "" {
text = http . StatusText ( rsp . StatusCode )
if text == "" {
text = "status code " + strconv . Itoa ( rsp . StatusCode )
}
} else {
// Just to reduce stutter, if user set rsp.Status to "200 OK" and StatusCode to 200.
// Not important.
text = strings . TrimPrefix ( text , strconv . Itoa ( rsp . StatusCode ) + " " )
}
return text
}
// StatusLine returns the status line of the response
func ( rsp * ProxyResponse ) StatusLine ( ) string {
// Status line, stolen from net/http/response.go
return fmt . Sprintf ( "HTTP/%d.%d %03d %s" , rsp . ProtoMajor , rsp . ProtoMinor , rsp . StatusCode , rsp . HTTPStatus ( ) )
}
// HeaderSection returns the header section of the response (without the extra trailing \r\n)
func ( rsp * ProxyResponse ) HeaderSection ( ) string {
retStr := rsp . StatusLine ( )
retStr += "\r\n"
for k , vs := range rsp . Header {
for _ , v := range vs {
retStr += fmt . Sprintf ( "%s: %s\r\n" , k , v )
}
}
return retStr
}
func ( msg * ProxyWSMessage ) String ( ) string {
var dirStr string
if msg . Direction == ToClient {
dirStr = "ToClient"
} else {
dirStr = "ToServer"
}
return fmt . Sprintf ( "{WS Message msg=\"%s\", type=%d, dir=%s}" , string ( msg . Message ) , msg . Type , dirStr )
}
// Clone returns a copy of the original message. It will have the same type, message, direction, timestamp, and request
func ( msg * ProxyWSMessage ) Clone ( ) * ProxyWSMessage {
var retMsg ProxyWSMessage
retMsg . Type = msg . Type
retMsg . Message = msg . Message
retMsg . Direction = msg . Direction
retMsg . Timestamp = msg . Timestamp
retMsg . Request = msg . Request
return & retMsg
}
// DeepClone returns a clone of the original message and a deep clone of the unmangled version if it exists
func ( msg * ProxyWSMessage ) DeepClone ( ) * ProxyWSMessage {
retMsg := msg . Clone ( )
retMsg . DbId = msg . DbId
if msg . Unmangled != nil {
retMsg . Unmangled = msg . Unmangled . DeepClone ( )
}
return retMsg
}
// Eq checks if the message has the same type, direction, and message as another message
func ( msg * ProxyWSMessage ) Eq ( other * ProxyWSMessage ) bool {
if msg . Type != other . Type ||
msg . Direction != other . Direction ||
bytes . Compare ( msg . Message , other . Message ) != 0 {
return false
}
return true
}
func copyHeader ( hd http . Header ) http . Header {
var ret http . Header = make ( http . Header )
for k , vs := range hd {
for _ , v := range vs {
ret . Add ( k , v )
}
}
return ret
}
func submitRequest ( req * ProxyRequest , useProxy bool , proxyHost string ,
proxyPort int , proxyCreds * ProxyCredentials , proxyIsSOCKS bool ) error {
var dialer NetDialer = req . NetDial
if dialer == nil {
dialer = net . Dial
}
var conn net . Conn
var err error
var proxyFormat bool = false
if useProxy {
if proxyIsSOCKS {
var socksCreds * proxy . Auth
if proxyCreds != nil {
socksCreds = & proxy . Auth {
User : proxyCreds . Username ,
Password : proxyCreds . Password ,
}
}
socksDialer , err := proxy . SOCKS5 ( "tcp" , fmt . Sprintf ( "%s:%d" , proxyHost , proxyPort ) , socksCreds , proxy . Direct )
if err != nil {
return fmt . Errorf ( "error creating SOCKS dialer: %s" , err . Error ( ) )
}
conn , err = socksDialer . Dial ( "tcp" , fmt . Sprintf ( "%s:%d" , req . DestHost , req . DestPort ) )
if err != nil {
return fmt . Errorf ( "error dialing host: %s" , err . Error ( ) )
}
defer conn . Close ( )
} else {
conn , err = dialer ( "tcp" , fmt . Sprintf ( "%s:%d" , proxyHost , proxyPort ) )
if err != nil {
return fmt . Errorf ( "error dialing proxy: %s" , err . Error ( ) )
}
defer conn . Close ( )
if req . DestUseTLS {
if err := PerformConnect ( conn , req . DestHost , req . DestPort ) ; err != nil {
return err
}
proxyFormat = false
} else {
proxyFormat = true
}
}
} else {
conn , err = dialer ( "tcp" , fmt . Sprintf ( "%s:%d" , req . DestHost , req . DestPort ) )
if err != nil {
return fmt . Errorf ( "error dialing host: %s" , err . Error ( ) )
}
defer conn . Close ( )
}
if req . DestUseTLS {
tls_conn := tls . Client ( conn , & tls . Config {
InsecureSkipVerify : true ,
ServerName : req . DestHost ,
} )
conn = tls_conn
}
if proxyFormat {
return req . SubmitProxy ( conn , proxyCreds )
} else {
return req . Submit ( conn )
}
}
// SubmitRequest opens a connection to the request's DestHost:DestPort, using TLS if DestUseTLS is set, submits the request, and sets req.Response with the response when a response is received
func SubmitRequest ( req * ProxyRequest ) error {
return submitRequest ( req , false , "" , 0 , nil , false )
}
// SubmitRequestProxy connects to the given HTTP proxy, performs neccessary handshakes, and submits the request to its destination. req.Response will be set once a response is received
func SubmitRequestProxy ( req * ProxyRequest , proxyHost string , proxyPort int , creds * ProxyCredentials ) error {
return submitRequest ( req , true , proxyHost , proxyPort , creds , false )
}
// SubmitRequestProxy connects to the given SOCKS proxy, performs neccessary handshakes, and submits the request to its destination. req.Response will be set once a response is received
func SubmitRequestSOCKSProxy ( req * ProxyRequest , proxyHost string , proxyPort int , creds * ProxyCredentials ) error {
return submitRequest ( req , true , proxyHost , proxyPort , creds , true )
}