conn.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. /*
  2. * Copyright (c) 2015, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. // Package stats counts and keeps track of session stats. These are per-domain
  20. // bytes transferred and total bytes transferred.
  21. package transferstats
  22. /*
  23. Assumption: The same connection will not be used to access different hostnames
  24. (even if, say, those hostnames map to the same server). If this does occur, we
  25. will mis-attribute some bytes.
  26. Assumption: Enough of the first HTTP will be present in the first Write() call
  27. for us to a) recognize that it is HTTP, and b) parse the hostname.
  28. - If this turns out to not be generally true we will need to add buffering.
  29. */
  30. import (
  31. "net"
  32. "sync/atomic"
  33. )
  34. // Conn is to be used as an intermediate link in a chain of net.Conn objects.
  35. // It inspects requests and responses and derives stats from them.
  36. type Conn struct {
  37. net.Conn
  38. serverID string
  39. firstWrite int32
  40. hostnameParsed int32
  41. hostname string
  42. regexps *Regexps
  43. }
  44. // NewConn creates a Conn. serverID can be anything that uniquely
  45. // identifies the server; it will be passed to TakeOutStatsForServer() when
  46. // retrieving the accumulated stats.
  47. func NewConn(nextConn net.Conn, serverID string, regexps *Regexps) *Conn {
  48. return &Conn{
  49. Conn: nextConn,
  50. serverID: serverID,
  51. firstWrite: 1,
  52. hostnameParsed: 0,
  53. regexps: regexps,
  54. }
  55. }
  56. // Write is called when requests are being written out through the tunnel to
  57. // the remote server.
  58. func (conn *Conn) Write(buffer []byte) (n int, err error) {
  59. // First pass the data down the chain.
  60. n, err = conn.Conn.Write(buffer)
  61. // Count stats before we check the error condition. It could happen that the
  62. // buffer was partially written and then an error occurred.
  63. if n > 0 {
  64. // If this is the first request, try to determine the hostname to associate
  65. // with this connection.
  66. if atomic.CompareAndSwapInt32(&conn.firstWrite, 1, 0) {
  67. hostname, ok := getHostname(buffer)
  68. if ok {
  69. // Get the hostname value that will be stored in stats by
  70. // regexing the real hostname.
  71. conn.hostname = regexHostname(hostname, conn.regexps)
  72. atomic.StoreInt32(&conn.hostnameParsed, 1)
  73. }
  74. }
  75. recordStat(&statsUpdate{
  76. conn.serverID,
  77. conn.hostname,
  78. int64(n),
  79. 0},
  80. false)
  81. }
  82. return
  83. }
  84. // Read is called when responses to requests are being read from the remote server.
  85. func (conn *Conn) Read(buffer []byte) (n int, err error) {
  86. n, err = conn.Conn.Read(buffer)
  87. var hostname string
  88. if 1 == atomic.LoadInt32(&conn.hostnameParsed) {
  89. hostname = conn.hostname
  90. } else {
  91. hostname = ""
  92. }
  93. // Count bytes without checking the error condition. It could happen that the
  94. // buffer was partially read and then an error occurred.
  95. recordStat(&statsUpdate{
  96. conn.serverID,
  97. hostname,
  98. 0,
  99. int64(n)},
  100. false)
  101. return
  102. }