tomas / tomas/needle

Digest MD5 not working with Geovision IP Camera

Open
#423 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
1.6k
Forks
237
PR merge metrics
No merged PRs in 30d

Description

I have been trying to get this HTTP request to work using Needle without luck. The HTTP request works using Postman and Axos-digest but not using Needle. However, Needle works with other IP cameras we have.
I m using v3.2.0

Code:

var opts = {
  username: 'username',
  password: 'password',
  auth: 'digest'
}

needle.get('http://192.168.1.199:80/LAPI/V1.0/Media/Video/Streams/0/Snapshot', opts, function(err, resp, body) {
  console.log(resp.headers);

  if (resp.statusCode == 401)
    console.log('\nIt failed.')
  else
    console.log('\nIt worked!')
});

Here is the log:

******** resp:  <ref *2> IncomingMessage {
  _readableState: ReadableState {
    objectMode: false,
    highWaterMark: 16384,
    buffer: BufferList { head: null, tail: null, length: 0 },
    length: 0,
    pipes: [],
    flowing: false,
    ended: true,
    endEmitted: true,
    reading: false,
    constructed: true,
    sync: true,
    needReadable: false,
    emittedReadable: false,
    readableListening: false,
    resumeScheduled: false,
    errorEmitted: false,
    emitClose: true,
    autoDestroy: true,
    destroyed: true,
    errored: null,
    closed: true,
    closeEmitted: true,
    defaultEncoding: 'utf8',
    awaitDrainWriters: SafeSet(0) [Set] {},
    multiAwaitDrain: true,
    readingMore: true,
    dataEmitted: true,
    decoder: null,
    encoding: null,
    [Symbol(kPaused)]: true
  },
  _events: [Object: null prototype] {
    end: [ [Function: responseOnEnd], [Function: onend], [Function: onend] ],
    close: [
      [Function (anonymous)],
      [Function: onclose],
      [Function (anonymous)],
      [Function: onclose]
    ],
    finish: [ [Function: onfinish], [Function: onfinish] ],
    error: [ [Function: onerror], [Function: onerror] ]
  },
  _eventsCount: 4,
  _maxListeners: undefined,
  socket: <ref *1> Socket {
    connecting: false,
    _hadError: false,
    _parent: null,
    _host: null,
    _readableState: ReadableState {
      objectMode: false,
      highWaterMark: 16384,
      buffer: BufferList { head: null, tail: null, length: 0 },
      length: 0,
      pipes: [],
      flowing: true,
      ended: false,
      endEmitted: false,
      reading: true,
      constructed: true,
      sync: false,
      needReadable: true,
      emittedReadable: false,
      readableListening: false,
      resumeScheduled: false,
      errorEmitted: false,
      emitClose: false,
      autoDestroy: true,
      destroyed: false,
      errored: null,
      closed: false,
      closeEmitted: false,
      defaultEncoding: 'utf8',
      awaitDrainWriters: null,
      multiAwaitDrain: false,
      readingMore: false,
      dataEmitted: true,
      decoder: null,
      encoding: null,
      [Symbol(kPaused)]: false
    },
    _events: [Object: null prototype] {
      end: [Function: onReadableStreamEnd],
      free: [Function: onFree],
      close: [Array],
      timeout: [Function: onTimeout],
      agentRemove: [Function: onRemove],
      error: [Function: socketErrorListener],
      finish: [Function]
    },
    _eventsCount: 7,
    _maxListeners: undefined,
    _writableState: WritableState {
      objectMode: false,
      highWaterMark: 16384,
      finalCalled: true,
      needDrain: false,
      ending: true,
      ended: true,
      finished: false,
      destroyed: false,
      decodeStrings: false,
      defaultEncoding: 'utf8',
      length: 0,
      writing: false,
      corked: 0,
      sync: false,
      bufferProcessing: false,
      onwrite: [Function: bound onwrite],
      writecb: null,
      writelen: 0,
      afterWriteTickInfo: null,
      buffered: [],
      bufferedIndex: 0,
      allBuffers: true,
      allNoop: true,
      pendingcb: 1,
      constructed: true,
      prefinished: false,
      errorEmitted: false,
      emitClose: false,
      autoDestroy: true,
      errored: null,
      closed: false,
      closeEmitted: false,
      [Symbol(kOnFinished)]: []
    },
    allowHalfOpen: false,
    _sockname: null,
    _pendingData: null,
    _pendingEncoding: '',
    server: null,
    _server: null,
    parser: null,
    _httpMessage: ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 3,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      maxRequestsOnConnectionReached: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: false,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      _contentLength: 0,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: false,
      socket: [Circular *1],
      _header: 'GET /LAPI/V1.0/Media/Video/Streams/0/Snapshot HTTP/1.1\r\n' +
        'accept: */*\r\n' +
        'user-agent: Needle/3.2.0 (Node.js v16.15.0; darwin arm64)\r\n' +
        'host: 192.168.1.199\r\n' +
        'authorization: Digest uri="/LAPI/V1.0/Media/Video/Streams/0/Snapshot", realm="0013e22cfbf8", nonce="9dd2edda3baca2e7a5b859fb440f71ca", username="brizi", response="e156f4df8c197a301f316b38e48bf631", qop="auth", opaque="07c984bd79faee74498abe2331d5e8db", nc="00000002", cnonce="c8657603"\r\n' +
        'Connection: close\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: nop],
      agent: [Agent],
      socketPath: undefined,
      method: 'GET',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      path: '/LAPI/V1.0/Media/Video/Streams/0/Snapshot',
      _ended: true,
      res: [Circular *2],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: '192.168.1.199',
      protocol: 'http:',
      [Symbol(kCapture)]: false,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype]
    },
    [Symbol(async_id_symbol)]: 286,
    [Symbol(kHandle)]: TCP {
      reading: true,
      onconnection: null,
      [Symbol(owner_symbol)]: [Circular *1]
    },
    [Symbol(lastWriteQueueSize)]: 0,
    [Symbol(timeout)]: null,
    [Symbol(kBuffer)]: null,
    [Symbol(kBufferCb)]: null,
    [Symbol(kBufferGen)]: null,
    [Symbol(kCapture)]: false,
    [Symbol(kSetNoDelay)]: false,
    [Symbol(kSetKeepAlive)]: false,
    [Symbol(kSetKeepAliveInitialDelay)]: 0,
    [Symbol(kBytesRead)]: 0,
    [Symbol(kBytesWritten)]: 0,
    [Symbol(RequestTimeout)]: undefined
  },
  httpVersionMajor: 1,
  httpVersionMinor: 1,
  httpVersion: '1.1',
  complete: true,
  rawHeaders: [
    'Content-Length',
    '243',
    'Content-Type',
    'text/plain',
    'Connection',
    'close',
    'WWW-Authenticate',
    'Digest realm="0013e22cfbf8", nonce="9dd2edda3baca2e7a5b859fb440f71ca", stale="false", algorithm="MD5", qop="auth", opaque="07c984bd79faee74498abe2331d5e8db"',
    'X-Frame-Options',
    'SAMEORIGIN',
    'X-XSS-Protection',
    '1;mode=block'
  ],
  rawTrailers: [],
  aborted: false,
  upgrade: false,
  url: '',
  method: null,
  statusCode: 401,
  statusMessage: 'UnAuthorized',
  client: <ref *1> Socket {
    connecting: false,
    _hadError: false,
    _parent: null,
    _host: null,
    _readableState: ReadableState {
      objectMode: false,
      highWaterMark: 16384,
      buffer: BufferList { head: null, tail: null, length: 0 },
      length: 0,
      pipes: [],
      flowing: true,
      ended: false,
      endEmitted: false,
      reading: true,
      constructed: true,
      sync: false,
      needReadable: true,
      emittedReadable: false,
      readableListening: false,
      resumeScheduled: false,
      errorEmitted: false,
      emitClose: false,
      autoDestroy: true,
      destroyed: false,
      errored: null,
      closed: false,
      closeEmitted: false,
      defaultEncoding: 'utf8',
      awaitDrainWriters: null,
      multiAwaitDrain: false,
      readingMore: false,
      dataEmitted: true,
      decoder: null,
      encoding: null,
      [Symbol(kPaused)]: false
    },
    _events: [Object: null prototype] {
      end: [Function: onReadableStreamEnd],
      free: [Function: onFree],
      close: [Array],
      timeout: [Function: onTimeout],
      agentRemove: [Function: onRemove],
      error: [Function: socketErrorListener],
      finish: [Function]
    },
    _eventsCount: 7,
    _maxListeners: undefined,
    _writableState: WritableState {
      objectMode: false,
      highWaterMark: 16384,
      finalCalled: true,
      needDrain: false,
      ending: true,
      ended: true,
      finished: false,
      destroyed: false,
      decodeStrings: false,
      defaultEncoding: 'utf8',
      length: 0,
      writing: false,
      corked: 0,
      sync: false,
      bufferProcessing: false,
      onwrite: [Function: bound onwrite],
      writecb: null,
      writelen: 0,
      afterWriteTickInfo: null,
      buffered: [],
      bufferedIndex: 0,
      allBuffers: true,
      allNoop: true,
      pendingcb: 1,
      constructed: true,
      prefinished: false,
      errorEmitted: false,
      emitClose: false,
      autoDestroy: true,
      errored: null,
      closed: false,
      closeEmitted: false,
      [Symbol(kOnFinished)]: []
    },
    allowHalfOpen: false,
    _sockname: null,
    _pendingData: null,
    _pendingEncoding: '',
    server: null,
    _server: null,
    parser: null,
    _httpMessage: ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 3,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      maxRequestsOnConnectionReached: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: false,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      _contentLength: 0,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: false,
      socket: [Circular *1],
      _header: 'GET /LAPI/V1.0/Media/Video/Streams/0/Snapshot HTTP/1.1\r\n' +
        'accept: */*\r\n' +
        'user-agent: Needle/3.2.0 (Node.js v16.15.0; darwin arm64)\r\n' +
        'host: 192.168.1.199\r\n' +
        'authorization: Digest uri="/LAPI/V1.0/Media/Video/Streams/0/Snapshot", realm="0013e22cfbf8", nonce="9dd2edda3baca2e7a5b859fb440f71ca", username="brizi", response="e156f4df8c197a301f316b38e48bf631", qop="auth", opaque="07c984bd79faee74498abe2331d5e8db", nc="00000002", cnonce="c8657603"\r\n' +
        'Connection: close\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: nop],
      agent: [Agent],
      socketPath: undefined,
      method: 'GET',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      path: '/LAPI/V1.0/Media/Video/Streams/0/Snapshot',
      _ended: true,
      res: [Circular *2],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: '192.168.1.199',
      protocol: 'http:',
      [Symbol(kCapture)]: false,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype]
    },
    [Symbol(async_id_symbol)]: 286,
    [Symbol(kHandle)]: TCP {
      reading: true,
      onconnection: null,
      [Symbol(owner_symbol)]: [Circular *1]
    },
    [Symbol(lastWriteQueueSize)]: 0,
    [Symbol(timeout)]: null,
    [Symbol(kBuffer)]: null,
    [Symbol(kBufferCb)]: null,
    [Symbol(kBufferGen)]: null,
    [Symbol(kCapture)]: false,
    [Symbol(kSetNoDelay)]: false,
    [Symbol(kSetKeepAlive)]: false,
    [Symbol(kSetKeepAliveInitialDelay)]: 0,
    [Symbol(kBytesRead)]: 0,
    [Symbol(kBytesWritten)]: 0,
    [Symbol(RequestTimeout)]: undefined
  },
  _consuming: false,
  _dumped: false,
  req: <ref *3> ClientRequest {
    _events: [Object: null prototype] {
      abort: [Function],
      prefinish: [Function: requestOnPrefinish],
      error: [Function]
    },
    _eventsCount: 3,
    _maxListeners: undefined,
    outputData: [],
    outputSize: 0,
    writable: true,
    destroyed: false,
    _last: true,
    chunkedEncoding: false,
    shouldKeepAlive: false,
    maxRequestsOnConnectionReached: false,
    _defaultKeepAlive: true,
    useChunkedEncodingByDefault: false,
    sendDate: false,
    _removedConnection: false,
    _removedContLen: false,
    _removedTE: false,
    _contentLength: 0,
    _hasBody: true,
    _trailer: '',
    finished: true,
    _headerSent: true,
    _closed: false,
    socket: <ref *1> Socket {
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: null,
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: false,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: null,
      _server: null,
      parser: null,
      _httpMessage: [Circular *3],
      [Symbol(async_id_symbol)]: 286,
      [Symbol(kHandle)]: [TCP],
      [Symbol(lastWriteQueueSize)]: 0,
      [Symbol(timeout)]: null,
      [Symbol(kBuffer)]: null,
      [Symbol(kBufferCb)]: null,
      [Symbol(kBufferGen)]: null,
      [Symbol(kCapture)]: false,
      [Symbol(kSetNoDelay)]: false,
      [Symbol(kSetKeepAlive)]: false,
      [Symbol(kSetKeepAliveInitialDelay)]: 0,
      [Symbol(kBytesRead)]: 0,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(RequestTimeout)]: undefined
    },
    _header: 'GET /LAPI/V1.0/Media/Video/Streams/0/Snapshot HTTP/1.1\r\n' +
      'accept: */*\r\n' +
      'user-agent: Needle/3.2.0 (Node.js v16.15.0; darwin arm64)\r\n' +
      'host: 192.168.1.199\r\n' +
      'authorization: Digest uri="/LAPI/V1.0/Media/Video/Streams/0/Snapshot", realm="0013e22cfbf8", nonce="9dd2edda3baca2e7a5b859fb440f71ca", username="brizi", response="e156f4df8c197a301f316b38e48bf631", qop="auth", opaque="07c984bd79faee74498abe2331d5e8db", nc="00000002", cnonce="c8657603"\r\n' +
      'Connection: close\r\n' +
      '\r\n',
    _keepAliveTimeout: 0,
    _onPendingData: [Function: nop],
    agent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 80,
      protocol: 'http:',
      options: [Object: null prototype],
      requests: [Object: null prototype] {},
      sockets: [Object: null prototype],
      freeSockets: [Object: null prototype] {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 1,
      [Symbol(kCapture)]: false
    },
    socketPath: undefined,
    method: 'GET',
    maxHeaderSize: undefined,
    insecureHTTPParser: undefined,
    path: '/LAPI/V1.0/Media/Video/Streams/0/Snapshot',
    _ended: true,
    res: [Circular *2],
    aborted: false,
    timeoutCb: null,
    upgradeOrConnect: false,
    parser: null,
    maxHeadersCount: null,
    reusedSocket: false,
    host: '192.168.1.199',
    protocol: 'http:',
    [Symbol(kCapture)]: false,
    [Symbol(kNeedDrain)]: false,
    [Symbol(corked)]: 0,
    [Symbol(kOutHeaders)]: [Object: null prototype] {
      accept: [Array],
      'user-agent': [Array],
      host: [Array],
      authorization: [Array]
    }
  },
  raw: <Buffer 7b 0d 0a 22 52 65 73 70 6f 6e 73 65 22 3a 20 7b 0d 0a 09 22 52 65 73 70 6f 6e 73 65 55 52 4c 22 3a 20 22 2f 4c 41 50 49 2f 56 31 2e 30 2f 4d 65 64 69 ... 193 more bytes>,
  body: '{\r\n' +
    '"Response": {\r\n' +
    '\t"ResponseURL": "/LAPI/V1.0/Media/Video/Streams/0/Snapshot",\r\n' +
    '\t"ResponseCode": 3,\r\n' +
    ' \t"SubResponseCode": 0,\r\n' +
    ' \t"ResponseString": "Not Authorized",\r\n' +
    '\t"StatusCode": 401,\r\n' +
    '\t"StatusString": "Unauthorized",\r\n' +
    '\t"Data": "null"\r\n' +
    '}\r\n' +
    '}\r\n',
  bytes: 243,
  [Symbol(kCapture)]: false,
  [Symbol(kHeaders)]: {
    'content-length': '243',
    'content-type': 'text/plain',
    connection: 'close',
    'www-authenticate': 'Digest realm="0013e22cfbf8", nonce="9dd2edda3baca2e7a5b859fb440f71ca", stale="false", algorithm="MD5", qop="auth", opaque="07c984bd79faee74498abe2331d5e8db"',
    'x-frame-options': 'SAMEORIGIN',
    'x-xss-protection': '1;mode=block'
  },
  [Symbol(kHeadersCount)]: 12,
  [Symbol(kTrailers)]: null,
  [Symbol(kTrailersCount)]: 0,
  [Symbol(RequestTimeout)]: undefined
}

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the supplied Needle 3.2.0 request against a Geovision camera and compare its Digest authorization exchange with the successful Postman or Axos-digest request. Trace the digest-auth request path and verify the MD5 response fields, then add a regression test that confirms the camera request succeeds.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
authentication, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.