├── LICENSE ├── README.md ├── nhttpsnoop ├── package.json └── testserver.js /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Joyent, Inc. All rights reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nhttpsnoop: Trace Node.js HTTP server activity 2 | 3 | Synopsis: 4 | 5 | usage: nhttpsnoop [-cglns] [-t argtype] [-o col,...] [-p pid,...] 6 | 7 | nhttpsnoop traces Node.js HTTP client, server, and garbage collection activity. 8 | By default, all requests for all Node.js HTTP servers on the system are traced, 9 | and information about each one is displayed as it completes: 10 | 11 | nhttpsnoop traces Node.js HTTP server activity. By default, all requests from 12 | all Node.js servers on the system are traced, and each one is displayed as it 13 | completes: 14 | 15 | # nhttpsnoop 16 | TIME PID PROBE LATENCY METHOD PATH 17 | [ 0.440283] 73160 server 1.264ms GET /uter 18 | [ 1.436516] 73160 server 1.475ms GET /wendell 19 | [ 1.436611] 73160 server 1.435ms GET /allison 20 | [ 1.436687] 73160 server 1.375ms GET /uter 21 | 22 | With -l, a record is emitted when requests are received as well in addition to 23 | when the response is sent: 24 | 25 | # nhttpsnoop -l 26 | TIME PID PROBE LATENCY METHOD PATH 27 | [ 0.814249] 73160 server -> - GET /allison 28 | [ 0.814426] 73160 server <- 0.177ms GET /allison 29 | [ 3.201576] 73160 server -> - GET /wendell 30 | [ 3.202105] 73160 server -> - GET /allison 31 | [ 3.202607] 73160 server <- 1.030ms GET /wendell 32 | [ 3.202745] 73160 server <- 0.639ms GET /allison 33 | 34 | Besides server operations (shown by default, and also requested with "-s"), you 35 | can trace client operations with "-c" and garbage collection operations with 36 | "-g". These can be combined: 37 | 38 | # nhttpsnoop -cgls 39 | TIME PID PROBE LATENCY METHOD PATH 40 | [ 0.132371] 73160 client -> - GET /wendell 41 | [ 0.133475] 73160 server -> - GET /wendell 42 | [ 0.133887] 73160 server <- 0.411ms GET /wendell 43 | [ 0.134267] 73160 client <- 1.895ms GET /wendell 44 | [ 7.133776] 73160 gc <- 0.831ms - - 45 | 46 | You can also select fields for display with -o, much like ps(1) and other 47 | tools: 48 | 49 | # nhttpsnoop -otime,method,path 50 | TIME METHOD PATH 51 | [ 2.381936] GET /wf_runners/869de259-5bdf-4efe 52 | [ 2.965854] GET /search/wf_jobs 53 | [ 2.960546] GET /agentprobes 54 | 55 | See below for the list of columns available. 56 | 57 | Finally, you can select individual processes to trace using -p, also like ps(1). 58 | 59 | 60 | ## Option summary 61 | 62 | -c Trace HTTP client activity (request/response pairs). 63 | See "Notes" below. 64 | 65 | -g Trace garbage collections. 66 | 67 | -l Display two lines, one each for the beginning and end of each 68 | HTTP request. For server requests, these correspond with 69 | receiving the request and sending the response. For client 70 | requests, these correspond with sending the request and 71 | receiving the response. 72 | 73 | This is useful when you want to see how operations are 74 | actually interleaved instead of just how long each one takes. 75 | 76 | -n Don't actually run DTrace, but instead just print out the D 77 | script that would be used. 78 | 79 | -o col,... Display the specified columns instead of the default output. 80 | Available columns include: 81 | 82 | latency time in microseconds between the request being 83 | received and the response being sent. 84 | 85 | method Request's HTTP method 86 | 87 | path Request's HTTP URL path 88 | (excludes query parameters) 89 | 90 | pid process identifier 91 | 92 | probe indicates the type of event 93 | ("client", "server", or "gc") 94 | 95 | raddr Remote host's IPv4 address 96 | 97 | rport Remote host's TCP port 98 | 99 | time relative time of the event from when $nhs_arg0 100 | started 101 | 102 | url Request's full HTTP URL, including query parameters 103 | 104 | which Indicates with an arrow whether this is an 105 | incoming or outgoing request. 106 | 107 | Some fields may not apply to all events. 108 | 109 | -p pid,... Only trace the specified processes. 110 | 111 | -s Trace HTTP server activity (request/response pairs). 112 | 113 | -t ARGTYPE Specify which probe arguments to use, which must be one of 114 | "translated" or "simple". Translated arguments are more 115 | extensible, more efficient, and the only reliable approach on 116 | systems that support them. Untranslated arguments are 117 | required on OS X, which doesn't support USDT translators. 118 | The default value is selected based on your system and you 119 | should never need to override this. 120 | 121 | 122 | ## Selecting processes 123 | 124 | "-p" is the only way to select processes, but you can use this with pgrep(1) 125 | for more sophisticated selections: 126 | 127 | # nhttpsnoop -p "$(pgrep -f restify)" # select only "restify" processes 128 | # nhttpsnoop -p "$(pgrep -z myzone)" # select processes in zone "myzone" 129 | # nhttpsnoop -p "$(pgrep -u dap)" # select processes with user "dap" 130 | 131 | With "-p", all Node processes are actually traced, but only requests from the 132 | selected processes are printed. 133 | 134 | ## Notes 135 | 136 | This tool uses the Node.js DTrace provider and dtrace(1M). You must have 137 | permissions to run dtrace(1M) and use this provider. It works on illumos-based 138 | systems and OS X systems with builds of Node with [Node issue 139 | 3617](https://github.com/joyent/node/issues/3617) resolved. 140 | 141 | Data for HTTP client requests is not reliable when multiple requests are issued 142 | concurrently for the same remote server. Unfortunately, there's no way to 143 | reliably associate request and response pairs in this case. As a result, some 144 | records may be missing from the output, and others may have incorrect time and 145 | latency information. 146 | 147 | If you see an error like the following: 148 | 149 | dtrace: failed to compile script /var/tmp/nhttpsnoop.81961.d: line 20: failed to resolve translated type for args[1] 150 | nhttpsnoop: failed to invoke dtrace(1M) 151 | 152 | then "dtrace" didn't find the expected Node translator file. Node installs 153 | this file into $PREFIX/lib/dtrace/node.d, but dtrace only knows about 154 | /usr/lib/dtrace/node.d. So if you have installed Node into a prefix other than 155 | /usr, then you must specify this via DTRACE\_OPTS in your environment: 156 | 157 | # export DTRACE_OPTS='-L $PREFIX/lib/dtrace' 158 | 159 | where `$PREFIX` is where you've installed Node (e.g., "/usr/local" or 160 | "/opt/local"). nhttpsnoop passes DTRACE\_OPTS to "dtrace", which in this case 161 | causes "dtrace" to look for the node.d translator file in the directory 162 | specified by -L. See the "-L" option in dtrace(1M) for details. 163 | 164 | On older versions of illumos, you may see errors like this: 165 | 166 | # dtrace: error on enabled probe ID 34 (ID 67807: node6112:node:_ZN4node26DTRACE_HTTP_SERVER_REQUESTERKN2v89ArgumentsE:http-server-request): invalid kernel access in action #2 at DIF offset 348 167 | 168 | This has been fixed in versions of SmartOS after 20120531T220306Z. See "uname 169 | -v" to see what release you're running. 170 | -------------------------------------------------------------------------------- /nhttpsnoop: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # nhttpsnoop: snoop Node HTTP requests 5 | # 6 | 7 | function usage 8 | { 9 | cat >&2 << USAGE 10 | usage: $nhs_arg0 [-cglns] [-t argtype] [-o col,...] [-p pid,...] 11 | 12 | Trace Node.js HTTP client, server, and garbage collection activity. By 13 | default, all requests for all Node.js HTTP servers on the system are traced, 14 | and information about each one is displayed as it completes. You can also 15 | specify: 16 | 17 | -c Trace HTTP client activity (request/response pairs). 18 | See "Notes" below. 19 | 20 | -g Trace garbage collections. 21 | 22 | -s Trace HTTP server activity (request/response pairs). 23 | 24 | With none of these options, "-s" is assumed. For examples: 25 | 26 | # $nhs_arg0 # trace HTTP server activity only 27 | # $nhs_arg0 -c # trace HTTP client activity only 28 | # $nhs_arg0 -cs # trace HTTP client and server activity 29 | # $nhs_arg0 -g # trace garbage collections only 30 | # $nhs_arg0 -cgs # trace HTTP client and server activity and GCs 31 | 32 | Other options: 33 | 34 | -l Display two lines, one each for the beginning and end of each 35 | HTTP request. For server requests, these correspond with 36 | receiving the request and sending the response. For client 37 | requests, these correspond with sending the request and 38 | receiving the response. 39 | 40 | This is useful when you want to see how operations are 41 | actually interleaved instead of just how long each one takes. 42 | 43 | -n Don't actually run DTrace, but instead just print out the D 44 | script that would be used. 45 | 46 | -o col,... Display the specified columns instead of the default output. 47 | Available columns include: 48 | 49 | latency time in microseconds between the request being 50 | received and the response being sent. 51 | 52 | method Request's HTTP method 53 | 54 | path Request's HTTP URL path 55 | (excludes query parameters) 56 | 57 | pid process identifier 58 | 59 | probe indicates the type of event 60 | ("client", "server", or "gc") 61 | 62 | raddr Remote host's IPv4 address 63 | 64 | rport Remote host's TCP port 65 | 66 | time relative time of the event from when $nhs_arg0 67 | started 68 | 69 | url Request's full HTTP URL, including query parameters 70 | 71 | which Indicates with an arrow whether this is an 72 | incoming or outgoing request. 73 | 74 | Some fields may not apply to all events. 75 | 76 | -p pid,... Only trace the specified processes. 77 | 78 | -t ARGTYPE Specify which probe arguments to use, which must be one of 79 | "translated" or "simple". Translated arguments are more 80 | extensible, more efficient, and the only reliable approach on 81 | systems that support them. Untranslated arguments are 82 | required on OS X, which doesn't support USDT translators. 83 | The default value is selected based on your system and you 84 | should never need to override this. 85 | 86 | Notes 87 | 88 | This tool uses the Node.js DTrace provider and dtrace(1M). You must have 89 | appropriate permissions to run it. 90 | 91 | Data for HTTP client requests is not reliable when multiple requests are 92 | issued concurrently for the same remote server. Unfortunately, there's no 93 | way to reliably associate request and response pairs in this case. As a 94 | result, some records may be missing from the output, and others may have 95 | incorrect time and latency information. 96 | USAGE 97 | exit 2 98 | } 99 | 100 | function fail 101 | { 102 | echo "$nhs_arg0: $*" >&2 103 | exit 1 104 | } 105 | 106 | # 107 | # optsplit str 108 | # 109 | # Split a comma- or whitespace-separated string into its constituent pieces. 110 | # This is used to split the -p option (typically something like "-p pid1 pid2") 111 | # and the "-o" option (typically something like "-o col1,col2"). 112 | # 113 | function optsplit 114 | { 115 | for arg in "$@"; do 116 | IFS=, 117 | for carg in $arg; do 118 | echo $carg 119 | done 120 | IFS= 121 | done 122 | } 123 | 124 | # 125 | # join sep arg1 ... 126 | # 127 | # Join the arguments (strings) with the given separator string. 128 | # 129 | function join 130 | { 131 | local sep=$1 132 | shift 133 | 134 | echo -n "$1" 135 | shift 136 | 137 | for elt in "$@"; do 138 | echo -ne "$sep" 139 | echo -n "$elt" 140 | done 141 | } 142 | 143 | # 144 | # emit_printf fmt arg1 ... 145 | # 146 | # Emit a DTrace "printf" statement. 147 | # 148 | function emit_printf 149 | { 150 | local fmt="$1" 151 | shift 152 | 153 | echo -ne "\tprintf($fmt,\n\t " 154 | join ",\n\t " "$@" 155 | echo ");" 156 | } 157 | 158 | # 159 | # emit_header 160 | # 161 | # Emit a header row for the currently selected columns. 162 | # 163 | function emit_header 164 | { 165 | local fmts= args= 166 | for column in $nhs_cols; do 167 | case $column in 168 | fd) 169 | fmts="$fmts %4s" 170 | args="$args \"FD\"" 171 | ;; 172 | 173 | latency) 174 | fmts="$fmts %10s" 175 | args="$args \"LATENCY\"" 176 | ;; 177 | 178 | method) 179 | fmts="$fmts %-6s" 180 | args="$args \"METHOD\"" 181 | ;; 182 | 183 | path) 184 | fmts="$fmts %-20s" 185 | args="$args \"PATH\"" 186 | ;; 187 | 188 | pid) 189 | fmts="$fmts %6s" 190 | args="$args \"PID\"" 191 | ;; 192 | 193 | probe) 194 | fmts="$fmts %-6s" 195 | args="$args \"PROBE\"" 196 | ;; 197 | 198 | raddr) 199 | fmts="$fmts %-16s" 200 | args="$args \"RADDR\"" 201 | ;; 202 | 203 | rport) 204 | fmts="$fmts %5s" 205 | args="$args \"RPORT\"" 206 | ;; 207 | 208 | time) 209 | fmts="$fmts %-12s" 210 | args="$args \"TIME\"" 211 | ;; 212 | 213 | url) 214 | fmts="$fmts %-20s" 215 | args="$args \"URL\"" 216 | ;; 217 | 218 | which) 219 | fmts="$fmts %2s" 220 | args="$args \"\"" 221 | ;; 222 | 223 | esac 224 | done 225 | 226 | emit_printf "\"$(join " " $fmts)\\\\n\"" $args 227 | } 228 | 229 | # 230 | # emit_row request|response conn method uri 231 | # 232 | # Emit the body of a DTrace clause that will print out the fields requested by 233 | # the user. Because the http-server-request and http-server-response probes 234 | # access their arguments differently, the caller specifies how to access the 235 | # connection arguments, the method, and the URI in the current context. 236 | # 237 | function emit_row 238 | { 239 | local which=$1 fd=$2 raddr=$3 rport=$4 method=$5 uri=$6 240 | local fmts= 241 | local args= 242 | local lat= 243 | 244 | for column in $nhs_cols; do 245 | case $column in 246 | fd) 247 | if [[ $which != "gc" ]]; then 248 | fmts="$fmts %4d" 249 | args="$args $fd" 250 | else 251 | fmts="$fmts %4s" 252 | args="$args \"-\"" 253 | fi 254 | ;; 255 | 256 | latency) 257 | if [[ $which = "request" ]]; then 258 | fmts="$fmts %10s" 259 | args="$args \"-\"" 260 | else 261 | if [[ $which = "response" ]]; then 262 | lat="(timestamp-" 263 | lat="${lat}rqstarts[pid,$raddr,$rport])" 264 | else 265 | lat="(timestamp-self->gc_start)" 266 | fi 267 | 268 | fmts="$fmts %4d.%-03dms" 269 | args="$args $lat/MICROSEC" 270 | args="$args ($lat%MICROSEC)/1000" 271 | fi 272 | ;; 273 | 274 | method) 275 | fmts="$fmts %-6s" 276 | args="$args $method" 277 | ;; 278 | 279 | path) 280 | fmts="$fmts %-20s" 281 | args="$args strtok($uri,\"?\")" 282 | ;; 283 | 284 | pid) 285 | fmts="$fmts %6d" 286 | args="$args pid" 287 | ;; 288 | 289 | probe) 290 | fmts="$fmts %-6s" 291 | if [[ $which != "gc" ]]; then 292 | args="$args strtok(probename+sizeof(" 293 | args="${args}\"http-\")-1,\"-\")" 294 | else 295 | args="$args \"gc\"" 296 | fi 297 | ;; 298 | 299 | raddr) 300 | fmts="$fmts %-16s" 301 | args="$args $raddr" 302 | ;; 303 | 304 | rport) 305 | fmts="$fmts %5d" 306 | args="$args $rport" 307 | ;; 308 | 309 | time) 310 | fmts="$fmts [%3d.%06d]" 311 | args="$args (timestamp-start)/NANOSEC" 312 | args="$args (timestamp-start)%NANOSEC/1000" 313 | ;; 314 | 315 | url) 316 | fmts="$fmts %-20s" 317 | args="$args $uri" 318 | ;; 319 | 320 | which) 321 | if [[ $which = "request" ]]; then 322 | fmts="$fmts ->" 323 | else 324 | fmts="$fmts <-" 325 | fi 326 | ;; 327 | 328 | *) 329 | fail "invalid column name: $column" 330 | ;; 331 | 332 | esac 333 | done 334 | 335 | emit_printf "\"$(join " " $fmts)\\\\n\"" $args 336 | } 337 | 338 | nhs_arg0=$(basename $0) # program name (for error messages) 339 | nhs_tmpfile=/var/tmp/$nhs_arg0.$$.d # dynamically-generated D script 340 | nhs_pids= # selected pids (-p) 341 | nhs_cols= # selected columns (-o) 342 | nhs_dryrun="false" # just print script and exit (-n) 343 | nhs_tracestart="false" # print "request" events (-l) 344 | nhs_argtype="translated" # use translated args (see below) 345 | nhs_doclient="false" # trace client activity 346 | nhs_dogc="false" # trace GC activity 347 | nhs_doserver= # trace server activity 348 | 349 | # 350 | # OS X does not support translated arguments with USDT, so we default to using 351 | # the untranslated versions. We don't do this on other systems (e.g., illumos, 352 | # BSD) because the untranslated arguments are not present in older versions of 353 | # the provider there and because the untranslated arguments are more limited if 354 | # we ever extend the translated ones. (The untranslated versions are always 355 | # available on OS X because the provider wasn't supported there before they were 356 | # added.) This behavior can be overridden using the "-t" option. 357 | # 358 | [[ $(uname -s) = "Darwin" ]] && nhs_argtype="simple" 359 | 360 | while getopts "cglno:p:t:s" c; do 361 | case $c in 362 | c) nhs_doclient="true" ;; 363 | g) nhs_dogc="true" ;; 364 | l) nhs_tracestart="true" ;; 365 | n) nhs_dryrun="true" ;; 366 | o) nhs_cols=$(optsplit $OPTARG) ;; 367 | p) nhs_pids=$(optsplit $OPTARG) ;; 368 | s) nhs_doserver="true" ;; 369 | t) nhs_argtype="$OPTARG" ;; 370 | *) usage 371 | esac 372 | done 373 | 374 | if [[ $nhs_doclient = "true" || $nhs_dogc = "true" ]]; then 375 | [[ -z "$nhs_doserver" ]] && nhs_doserver="false" 376 | else 377 | nhs_doserver="true" 378 | fi 379 | 380 | nhs_rqprobes= 381 | nhs_rsprobes= 382 | if [[ "$nhs_doserver" = "true" ]]; then 383 | nhs_rqprobes="$nhs_rqprobes node*:::http-server-request" 384 | nhs_rsprobes="$nhs_rsprobes node*:::http-server-response" 385 | fi 386 | 387 | if [[ "$nhs_doclient" = "true" ]]; then 388 | nhs_rqprobes="$nhs_rqprobes node*:::http-client-request" 389 | nhs_rsprobes="$nhs_rsprobes node*:::http-client-response" 390 | fi 391 | 392 | # Configure default columns. 393 | if [[ -z "$nhs_cols" ]]; then 394 | if [[ $nhs_tracestart = "true" ]]; then 395 | nhs_cols="time pid probe which latency method path" 396 | else 397 | nhs_cols="time pid probe latency method path" 398 | fi 399 | fi 400 | 401 | # 402 | # For readability, we define bash variables to contain the D expressions used 403 | # for the various pieces of information we need. The expression depends on the 404 | # nhs_argtype (-t) option as well as which probe we're looking at ("request" or 405 | # "response"). 406 | # 407 | if [[ $nhs_argtype = "translated" ]]; then 408 | rqarg_fd="args[1]->fd" 409 | rqarg_raddr="args[1]->remoteAddress" 410 | rqarg_rport="args[1]->remotePort" 411 | rqarg_method="args[0]->method" 412 | rqarg_url="args[0]->url" 413 | 414 | rsarg_fd="args[0]->fd" 415 | rsarg_raddr="args[0]->remoteAddress" 416 | rsarg_rport="args[0]->remotePort" 417 | elif [[ $nhs_argtype = "simple" ]]; then 418 | rqarg_fd="-1" 419 | rqarg_raddr="copyinstr(arg2)" 420 | rqarg_rport="arg3" 421 | rqarg_method="copyinstr(arg4)" 422 | rqarg_url="copyinstr(arg5)" 423 | 424 | rsarg_fd="-1" 425 | rsarg_raddr="copyinstr(arg1)" 426 | rsarg_rport="arg2" 427 | else 428 | echo "$nhs_arg0: invalid value for \"-t\" option: \"$nhs_argtype\"" 429 | usage 430 | fi 431 | 432 | 433 | cat > $nhs_tmpfile <> $nhs_tmpfile 452 | echo >> $nhs_tmpfile 453 | 454 | # 455 | # We generate the DTrace script dynamically based on the options given. 456 | # If -p was specified, we predicate our first probe on the given set of 457 | # pids. 458 | # 459 | if [[ -n "$nhs_pids" ]]; then 460 | set -- $nhs_pids 461 | 462 | if [[ $# -eq 1 ]]; then 463 | echo "/pid == $1/" >> $nhs_tmpfile 464 | else 465 | echo "/pid == $1" >> $nhs_tmpfile 466 | shift 467 | for pid in "$@"; do 468 | echo " || pid == $pid" >> $nhs_tmpfile 469 | done 470 | echo "/" >> $nhs_tmpfile 471 | fi 472 | fi 473 | 474 | echo "{" >> $nhs_tmpfile 475 | 476 | [[ $nhs_tracestart = "true" ]] && 477 | emit_row "request" \ 478 | $rqarg_fd \ 479 | $rqarg_raddr \ 480 | $rqarg_rport \ 481 | $rqarg_method \ 482 | $rqarg_url >> $nhs_tmpfile 483 | 484 | cat >> $nhs_tmpfile <> $nhs_tmpfile <gc_start = timestamp; 512 | } 513 | 514 | node*:::gc-done 515 | /self->gc_start/ 516 | { 517 | $(emit_row "gc" -1 '"-"' 0 '"-"' '"-"') 518 | self->gc_start = 0; 519 | } 520 | EOF 521 | fi 522 | 523 | if [[ $nhs_dryrun = "true" ]]; then 524 | cat $nhs_tmpfile 525 | rm -f $nhs_tmpfile 526 | exit 0 527 | fi 528 | 529 | chmod +x $nhs_tmpfile 530 | dtrace $DTRACE_OPTS -Z -Cs $nhs_tmpfile || \ 531 | echo "$nhs_arg0: failed to invoke dtrace(1M)" >&2 532 | rv=$? 533 | rm -f $nhs_tmpfile 534 | exit $rv 535 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nhttpsnoop", 3 | "version": "1.0.0", 4 | "description": "Trace Node.js GC and HTTP server and client activity", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/davepacheco/nhttpsnoop" 8 | }, 9 | "keywords": [ "node", "http", "dtrace", "trace", "gc" ], 10 | "author": "Dave Pacheco ", 11 | "license": "MIT", 12 | "bugs": { 13 | "url": "https://github.com/davepacheco/nhttpsnoop/issues" 14 | }, 15 | "homepage": "https://github.com/davepacheco/nhttpsnoop", 16 | "bin": { 17 | "nhttpsnoop": "./nhttpsnoop" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /testserver.js: -------------------------------------------------------------------------------- 1 | /* 2 | * testserver.js: exercise the HTTP client and server 3 | */ 4 | 5 | var mod_http = require('http'); 6 | 7 | var port = 8080; 8 | var server; 9 | 10 | function main() 11 | { 12 | if (process.argv.length > 2) { 13 | port = Math.floor(process.argv[2]); 14 | 15 | if (isNaN(port)) { 16 | console.error('usage: %s %s [port]', 17 | process.argv[0], process.argv[1]); 18 | process.exit(1); 19 | } 20 | } 21 | 22 | server = mod_http.createServer(function (request, response) { 23 | setTimeout(function () { 24 | response.writeHead(200, 25 | { 'content-type': 'text/plain' }); 26 | response.end('hello world\n'); 27 | }, 1); 28 | }); 29 | 30 | server.listen(port, function () { 31 | console.log('server running at http://%s:%d', 32 | server.address()['address'], port); 33 | }); 34 | 35 | setInterval(tick, 1000); 36 | } 37 | 38 | function tick() 39 | { 40 | /* 41 | * We perform a random number of requests (up to 3) using some 42 | * randomized paths and query strings. This less us exercise the "path" 43 | * vs. "url" fields as well as overlapping operations. 44 | */ 45 | var nrequests = Math.ceil(Math.random() * 3); 46 | var uris = [ '/wendell', '/uter', '/allison' ]; 47 | var queries = [ '', '?limit=5', '?limit=5&offset=5' ]; 48 | var i; 49 | 50 | for (i = 0; i < nrequests; i++) { 51 | mod_http.get({ 52 | 'agent': false, 53 | 'port': port, 54 | 'path': uris[Math.floor(Math.random() * uris.length)] + 55 | queries[Math.floor(Math.random() * queries.length)] 56 | }); 57 | } 58 | } 59 | 60 | main(); 61 | --------------------------------------------------------------------------------